知识连接:
C++11 并发之std::atomic
本文概要:
一、成员类型和成员函数。
二、std::thread 构造函数。
三、异步。
四、多线程传递参数。
五、join、detach。
六、获取CPU核心个数。
七、CPP原子变量与线程安全。
八、lambda与多线程。
九、时间等待相关问题。
十、线程功能拓展。
十一、多线程可变参数。
十二、线程交换。
1三、线程移动。
std::thread 在 #include<thread> 头文件中声明,所以使用 std::thread 时须要包含 #include<thread> 头文件。
一、成员类型和成员函数。
成员类型:
-
id
-
Thread id
(public member type ) id
-
native_handle_type
-
Native handle type
(public member type )
成员函数:
-
(constructor)
-
Construct thread
(public member function ) 构造函数
-
(destructor)
-
Thread destructor
(public member function ) 析构函数
-
operator=
-
Move-assign thread
(public member function ) 赋值重载
-
get_id
-
Get thread id
(public member function ) 获取线程id
-
joinable
-
Check if joinable
(public member function ) 判断线程是否能够加入等待
-
join
-
Join thread
(public member function ) 加入等待
-
detach
-
Detach thread
(public member function ) 分离线程
-
swap
-
Swap threads
(public member function ) 线程交换
-
native_handle
-
Get native handle
(public member function ) 获取线程句柄
-
hardware_concurrency [static]
-
Detect hardware concurrency
(public static member function ) 检测硬件并发特性
Non-member overloads:
-
swap (thread)
-
Swap threads
(function )
二、std::thread 构造函数。
以下表:
-
default (1)
-
thread() noexcept;
-
initialization(2)
-
template <class Fn, class... Args> explicit thread (Fn&& fn, Args&&... args);
-
copy [deleted] (3)
-
thread (const thread&) = delete;
-
move [4]
-
hread (thread&& x) noexcept;
(1).默认构造函数,建立一个空的 thread 执行对象。
(2).初始化构造函数,建立一个 thread 对象,该 thread 对象可被 joinable,新产生的线程会调用 fn 函数,该函数的参数由 args 给出。
(3).拷贝构造函数(被禁用),意味着 thread 不可被拷贝构造。
(4).move 构造函数,move 构造函数,调用成功以后 x 不表明任何 thread 执行对象。
注意:可被 joinable 的 thread 对象必须在他们销毁以前被主线程 join 或者将其设置为 detached。
std::thread 各类构造函数例子以下:
- <span style="font-size:12px;">#include<iostream>
- #include<thread>
- #include<chrono>
- using namespace std;
- void fun1(int n)
- {
- cout << "Thread " << n << " executing\n";
- n += 10;
- this_thread::sleep_for(chrono::milliseconds(10));
- }
- void fun2(int & n)
- {
- cout << "Thread " << n << " executing\n";
- n += 20;
- this_thread::sleep_for(chrono::milliseconds(10));
- }
- int main()
- {
- int n = 0;
- thread t1;
- thread t2(fun1, n + 1);
- t2.join();
- cout << "n=" << n << '\n';
- n = 10;
- thread t3(fun2, ref(n));
- thread t4(move(t3));
- t4.join();
- cout << "n=" << n << '\n';
- return 0;
- }
- 运行结果:
- Thread 1 executing
- n=0
- Thread 10 executing
- n=30</span>
三、异步。
例如:
- <span style="font-size:12px;">#include<iostream>
- #include<thread>
- using namespace std;
- void show()
- {
- cout << "hello cplusplus!" << endl;
- }
- int main()
- {
-
- thread t1(show);
- thread t2(show);
- thread t3(show);
-
- thread th[3]{thread(show), thread(show), thread(show)};
-
- thread *pt1(new thread(show));
- thread *pt2(new thread(show));
- thread *pt3(new thread(show));
-
- thread *pth(new thread[3]{thread(show), thread(show), thread(show)});
- return 0;
- }</span>
四、多线程传递参数。
例如:
- <span style="font-size:12px;">#include<iostream>
- #include<thread>
- using namespace std;
- void show(const char *str, const int id)
- {
- cout << "线程 " << id + 1 << " :" << str << endl;
- }
- int main()
- {
- thread t1(show, "hello cplusplus!", 0);
- thread t2(show, "你好,C++!", 1);
- thread t3(show, "hello!", 2);
- return 0;
- }
- 运行结果:
- 线程 1线程 2 :你好,C++!线程 3 :hello!
- :hello cplusplus!</span>
五、join、detach。
join例子以下:
- <span style="font-size:12px;">#include<iostream>
- #include<thread>
- #include<array>
- using namespace std;
- void show()
- {
- cout << "hello cplusplus!" << endl;
- }
- int main()
- {
- array<thread, 3> threads = { thread(show), thread(show), thread(show) };
- for (int i = 0; i < 3; i++)
- {
- cout << threads[i].joinable() << endl;
- threads[i].join();
- }
- return 0;
- }
- 运行结果:
- hello cplusplus!
- hello cplusplus!
- 1
- hello cplusplus!
- 1
- 1</span>
总结:
join 是让当前主线程等待全部的子线程执行完,才能退出。
detach例子以下:
- <span style="font-size:12px;">#include<iostream>
- #include<thread>
- using namespace std;
- void show()
- {
- cout << "hello cplusplus!" << endl;
- }
- int main()
- {
- thread th(show);
-
- th.detach();
-
- cout << th.joinable() << endl;
- return 0;
- }
- 运行结果:
- hello cplusplus!
- 0</span>
线程 detach 脱离主线程的绑定,主线程挂了,子线程不报错,子线程执行完自动退出。
线程 detach之后,子线程会成为孤儿线程,线程之间将没法通讯。
六、获取CPU核心个数。
例如:
- <span style="font-size:12px;">#include<iostream>
- #include<thread>
- using namespace std;
- int main()
- {
- auto n = thread::hardware_concurrency();
- cout << n << endl;
- return 0;
- }
- 运行结果:
- 8</span>
经过 thread::hardware_concurrency() 获取 CPU 核心的个数。
七、CPP原子变量与线程安全。
问题例如:
- <span style="font-size:12px;">#include<iostream>
- #include<thread>
- using namespace std;
- const int N = 100000000;
- int num = 0;
- void run()
- {
- for (int i = 0; i < N; i++)
- {
- num++;
- }
- }
- int main()
- {
- clock_t start = clock();
- thread t1(run);
- thread t2(run);
- t1.join();
- t2.join();
- clock_t end = clock();
- cout << "num=" << num << ",用时 " << end - start << " ms" << endl;
- return 0;
- }
- 运行结果:
- num=143653419,用时 730 ms</span>
从上述代码执行的结果,发现结果并非咱们预计的200000000,这是因为线程之间发生冲突,从而致使结果不正确。
为了解决此问题,有如下方法:
(1)互斥量。
例如:
- <span style="font-size:12px;">#include<iostream>
- #include<thread>
- #include<mutex>
- using namespace std;
- const int N = 100000000;
- int num(0);
- mutex m;
- void run()
- {
- for (int i = 0; i < N; i++)
- {
- m.lock();
- num++;
- m.unlock();
- }
- }
- int main()
- {
- clock_t start = clock();
- thread t1(run);
- thread t2(run);
- t1.join();
- t2.join();
- clock_t end = clock();
- cout << "num=" << num << ",用时 " << end - start << " ms" << endl;
- return 0;
- }
- 运行结果:
- num=200000000,用时 128323 ms</span>
不难发现,经过互斥量后运算结果正确,可是计算速度很慢,缘由主要是互斥量加解锁须要时间。
(2)原子变量。
例如:
- <span style="font-size:12px;">#include<iostream>
- #include<thread>
- #include<atomic>
- using namespace std;
- const int N = 100000000;
- atomic_int num{ 0 };
- void run()
- {
- for (int i = 0; i < N; i++)
- {
- num++;
- }
- }
- int main()
- {
- clock_t start = clock();
- thread t1(run);
- thread t2(run);
- t1.join();
- t2.join();
- clock_t end = clock();
- cout << "num=" << num << ",用时 " << end - start << " ms" << endl;
- return 0;
- }
- 运行结果:
- num=200000000,用时 29732 ms</span>
不难发现,经过原子变量后运算结果正确,计算速度通常。
原子变量详细内容 请参考C++11 并发之std::atomic。
(3)加入 join 。
例如:
- <span style="font-size:12px;">#include<iostream>
- #include<thread>
- using namespace std;
- const int N = 100000000;
- int num = 0;
- void run()
- {
- for (int i = 0; i < N; i++)
- {
- num++;
- }
- }
- int main()
- {
- clock_t start = clock();
- thread t1(run);
- t1.join();
- thread t2(run);
- t2.join();
- clock_t end = clock();
- cout << "num=" << num << ",用时 " << end - start << " ms" << endl;
- return 0;
- }
- 运行结果:
- num=200000000,用时 626 ms</span>
不难发现,经过原子变量后运算结果正确,计算速度也很理想。
八、lambda与多线程。
例如:
- <span style="font-size:12px;">#include<iostream>
- #include<thread>
- using namespace std;
- int main()
- {
- auto fun = [](const char *str) {cout << str << endl; };
- thread t1(fun, "hello world!");
- thread t2(fun, "hello beijing!");
- return 0;
- }
- 运行结果:
- hello world!
- hello beijing!</span>
九、时间等待相关问题。
例如:
- <span style="font-size:12px;">#include<iostream>
- #include<thread>
- #include<chrono>
- using namespace std;
- int main()
- {
- thread th1([]()
- {
-
- this_thread::sleep_for(chrono::seconds(3));
-
- this_thread::yield();
-
- cout << this_thread::get_id() << endl;
- });
- return 0;
- }</span>
十、线程功能拓展。
例如:
- <span style="font-size:12px;">#include<iostream>
- #include<thread>
- using namespace std;
- class MyThread :public thread
- {
- public:
-
- MyThread() : thread()
- {
- }
-
- template<typename T, typename...Args>
- MyThread(T&&func, Args&&...args) : thread(forward<T>(func), forward<Args>(args)...)
- {
- }
- void showcmd(const char *str)
- {
- system(str);
- }
- };
- int main()
- {
- MyThread th1([]()
- {
- cout << "hello" << endl;
- });
- th1.showcmd("calc");
-
- MyThread th2([](const char * str)
- {
- cout << "hello" << str << endl;
- }, " this is MyThread");
- th2.showcmd("notepad");
- return 0;
- }
- 运行结果:
- hello
- hello this is MyThread
十一、多线程可变参数。
例如:
- <span style="font-size:12px;">#include<iostream>
- #include<thread>
- #include<cstdarg>
- using namespace std;
- int show(const char *fun, ...)
- {
- va_list ap;
- va_start(ap, fun);
- vprintf(fun, ap);
- va_end(ap);
- return 0;
- }
- int main()
- {
- thread t1(show, "%s %d %c %f", "hello world!", 100, 'A', 3.14159);
- return 0;
- }
- 运行结果:
- hello world! 100 A 3.14159</span>
十二、线程交换。
例如:
- <span style="font-size:12px;">#include<iostream>
- #include<thread>
- using namespace std;
- int main()
- {
- thread t1([]()
- {
- cout << "thread1" << endl;
- });
- thread t2([]()
- {
- cout << "thread2" << endl;
- });
- cout << "thread1' id is " << t1.get_id() << endl;
- cout << "thread2' id is " << t2.get_id() << endl;
-
- cout << "swap after:" << endl;
- swap(t1, t2);
- cout << "thread1' id is " << t1.get_id() << endl;
- cout << "thread2' id is " << t2.get_id() << endl;
- return 0;
- }
- 运行结果:
- thread1
- thread2
- thread1' id is 4836
- thread2' id is 4724
- swap after:
- thread1' id is 4724
- thread2' id is 4836</span>
1三、线程移动。
例如:
- <span style="font-size:12px;">#include<iostream>
- #include<thread>
- using namespace std;
- int main()
- {
- thread t1([]()
- {
- cout << "thread1" << endl;
- });
- cout << "thread1' id is " << t1.get_id() << endl;
- thread t2 = move(t1);;
- cout << "thread2' id is " << t2.get_id() << endl;
- return 0;
- }
- 运行结果:
- thread1
- thread1' id is 5620
- thread2' id is 5620</span>
从上述代码中,线程t2能够经过 move 移动 t1 来获取 t1 的所有属性,而 t1 却销毁了。