[转] c++11并发之std::thread

[转自 https://blog.csdn.net/liuker888/article/details/46848905#]

知识链接:
C++11 并发之std::atomic
 
本文概要:
1、成员类型和成员函数。
 
2、std::thread 构造函数。
3、异步。
4、多线程传递参数。
5、join、detach。
6、获取CPU核心个数。
7、CPP原子变量与线程安全。
8、lambda与多线程。
9、时间等待相关问题。
10、线程功能拓展。
11、多线程可变参数。
12、线程交换。
13、线程移动。
 
std::thread 在 #include<thread> 头文件中声明,因此使用 std::thread 时需要包含 #include<thread> 头文件。
 
1、成员类型和成员函数。
成员类型:

成员函数:

Non-member overloads:

 
2、std::thread 构造函数。
如下表:
(1).默认构造函数,创建一个空的 thread 执行对象。
(2).初始化构造函数,创建一个 thread 对象,该 thread 对象可被 joinable,新产生的线程会调用 fn 函数,该函数的参数由 args 给出。
(3).拷贝构造函数(被禁用),意味着 thread 不可被拷贝构造。
(4).move 构造函数,move 构造函数,调用成功之后 x 不代表任何 thread 执行对象。
注意:可被 joinable 的 thread 对象必须在他们销毁之前被主线程 join 或者将其设置为 detached。
 
std::thread 各种构造函数例子如下:
 
 1 <span style="font-size:12px;">#include<iostream>
 2 #include<thread>
 3 #include<chrono>
 4 using namespace std;
 5 void fun1(int n)  //初始化构造函数
 6 {
 7     cout << "Thread " << n << " executing\n";
 8     n += 10;
 9     this_thread::sleep_for(chrono::milliseconds(10));
10 }
11 
12 void fun2(int & n) //拷贝构造函数
13 {
14     cout << "Thread " << n << " executing\n";
15     n += 20;
16     this_thread::sleep_for(chrono::milliseconds(10));
17 }
18 
19 int main()
20 {
21     int n = 0;
22     thread t1;               //t1不是一个thread
23     thread t2(fun1, n + 1);  //按照值传递
24     t2.join();
25     cout << "n=" << n << '\n';
26     n = 10;
27     thread t3(fun2, ref(n)); //引用
28     thread t4(move(t3));     //t4执行t3,t3不是thread
29     t4.join();
30     cout << "n=" << n << '\n';
31     return 0;
32 }
33 
34 运行结果:
35 Thread 1 executing
36 n=0
37 Thread 10 executing
38 n=30</span>
3、异步。
例如:
 1 <span style="font-size:12px;">#include<iostream>
 2 #include<thread>
 3 using namespace std;
 4 void show()
 5 {
 6     cout << "hello cplusplus!" << endl;
 7 }
 8 int main()
 9 {
10     //栈上
11     thread t1(show);   //根据函数初始化执行
12     thread t2(show);
13     thread t3(show);
14     //线程数组
15     thread th[3]{thread(show), thread(show), thread(show)}; 
16     //堆上
17     thread *pt1(new thread(show));
18     thread *pt2(new thread(show));
19     thread *pt3(new thread(show));
20     //线程指针数组
21     thread *pth(new thread[3]{thread(show), thread(show), thread(show)});
22     return 0;
23 }</span>
4、多线程传递参数。
例如:
 1 <span style="font-size:12px;">#include<iostream>
 2 #include<thread>
 3 using namespace std;
 4 void show(const char *str, const int id)
 5 {
 6     cout << "线程 " << id + 1 << "" << str << endl;
 7 }
 8 int main()
 9 {
10     thread t1(show, "hello cplusplus!", 0);
11     thread t2(show, "你好,C++!", 1);
12     thread t3(show, "hello!", 2);
13     return 0;
14 }
15 运行结果:
16 线程 1线程 2 :你好,C++!线程 3 :hello!
17 :hello cplusplus!</span>
发现,线程 t1、t2、t3 都执行成功!
 
5、join、detach。
join例子如下:
 1 <span style="font-size:12px;">#include<iostream>
 2 #include<thread>
 3 #include<array>
 4 using namespace std;
 5 void show()
 6 {
 7     cout << "hello cplusplus!" << endl;
 8 }
 9 int main()
10 {
11     array<thread, 3>  threads = { thread(show), thread(show), thread(show) };
12     for (int i = 0; i < 3; i++)
13     {
14         cout << threads[i].joinable() << endl;//判断线程是否可以join
15         threads[i].join();//主线程等待当前线程执行完成再退出
16     }
17     return 0;
18 }
19 运行结果:
20 hello cplusplus!
21 hello cplusplus!
22 1
23 hello cplusplus!
24 1
25 1</span>
总结:
join 是让当前主线程等待所有的子线程执行完,才能退出。
detach例子如下:
 1 <span style="font-size:12px;">#include<iostream>
 2 #include<thread>
 3 using namespace std;
 4 void show()
 5 {
 6     cout << "hello cplusplus!" << endl;
 7 }
 8 int main()
 9 {
10     thread th(show);
11     //th.join(); 
12     th.detach();//脱离主线程的绑定,主线程挂了,子线程不报错,子线程执行完自动退出。
13     //detach以后,子线程会成为孤儿线程,线程之间将无法通信。
14     cout << th.joinable() << endl;
15     return 0;
16 }
17 运行结果:
18 hello cplusplus!
19 0</span>
结论:
线程 detach 脱离主线程的绑定,主线程挂了,子线程不报错,子线程执行完自动退出。
线程 detach以后,子线程会成为孤儿线程,线程之间将无法通信。
 
6、获取CPU核心个数。
例如:
 1 <span style="font-size:12px;">#include<iostream>
 2 #include<thread>
 3 using namespace std;
 4 int main()
 5 {
 6     auto n = thread::hardware_concurrency();//获取cpu核心个数
 7     cout << n << endl;
 8     return 0;
 9 }
10 运行结果:
11 8</span>
结论:
通过  thread::hardware_concurrency() 获取 CPU 核心的个数。
 
7、CPP原子变量与线程安全。
问题例如:
 1 <span style="font-size:12px;">#include<iostream>
 2 #include<thread>
 3 using namespace std;
 4 const int N = 100000000;
 5 int num = 0;
 6 void run()
 7 {
 8     for (int i = 0; i < N; i++)
 9     {
10         num++;
11     }
12 }
13 int main()
14 {
15     clock_t start = clock();
16     thread t1(run);
17     thread t2(run);
18     t1.join();
19     t2.join();
20     clock_t end = clock();
21     cout << "num=" << num << ",用时 " << end - start << " ms" << endl;
22     return 0;
23 }
24 运行结果:
25 num=143653419,用时 730 ms</span>
从上述代码执行的结果,发现结果并不是我们预计的200000000,这是由于线程之间发生冲突,从而导致结果不正确。
为了解决此问题,有以下方法:
(1)互斥量。
例如:
 1 <span style="font-size:12px;">#include<iostream>
 2 #include<thread>
 3 #include<mutex>
 4 using namespace std;
 5 const int N = 100000000;
 6 int num(0);
 7 mutex m;
 8 void run()
 9 {
10     for (int i = 0; i < N; i++)
11     {
12         m.lock();
13         num++;
14         m.unlock();
15     }
16 }
17 
18 int main()
19 {
20     clock_t start = clock();
21     thread t1(run);
22     thread t2(run);
23     t1.join();
24     t2.join();
25     clock_t end = clock();
26     cout << "num=" << num << ",用时 " << end - start << " ms" << endl;
27     return 0;
28 }
29 运行结果:
30 num=200000000,用时 128323 ms</span>
不难发现,通过互斥量后运算结果正确,但是计算速度很慢,原因主要是互斥量加解锁需要时间。
互斥量详细内容 请参考C++11 并发之std::mutex
(2)原子变量。
例如:
 1 <span style="font-size:12px;">#include<iostream>
 2 #include<thread>
 3 #include<atomic>
 4 using namespace std;
 5 const int N = 100000000;
 6 atomic_int num{ 0 };//不会发生线程冲突,线程安全
 7 
 8 void run()
 9 {
10     for (int i = 0; i < N; i++)
11     {
12         num++;
13     }
14 }
15 int main()
16 {
17     clock_t start = clock();
18     thread t1(run);
19     thread t2(run);
20     t1.join();
21     t2.join();
22     clock_t end = clock();
23     cout << "num=" << num << ",用时 " << end - start << " ms" << endl;
24     return 0;
25 }
26 
27 运行结果:
28 num=200000000,用时 29732 ms</span>
不难发现,通过原子变量后运算结果正确,计算速度一般。
原子变量详细内容 请参考C++11 并发之std::atomic。
(3)加入 join 。
例如:
 1 <span style="font-size:12px;">#include<iostream>
 2 #include<thread>
 3 using namespace std;
 4 const int N = 100000000;
 5 int num = 0;
 6 
 7 void run()
 8 {
 9     for (int i = 0; i < N; i++)
10     {
11         num++;
12     }
13 }
14 int main()
15 {
16     clock_t start = clock();
17     thread t1(run);
18     t1.join();
19     thread t2(run);
20     t2.join();
21     clock_t end = clock();
22     cout << "num=" << num << ",用时 " << end - start << " ms" << endl;
23     return 0;
24 }
25 运行结果:
26 num=200000000,用时 626 ms</span>
不难发现,通过原子变量后运算结果正确,计算速度也很理想。
 
8、lambda与多线程。
例如:
 1 <span style="font-size:12px;">#include<iostream>
 2 #include<thread>
 3 using namespace std;
 4 int main()
 5 {
 6     auto fun = [](const char *str) {cout << str << endl; };
 7     thread t1(fun, "hello world!");
 8     thread t2(fun, "hello beijing!");
 9     return 0;
10 }
11 运行结果:
12 hello world!
13 hello beijing!</span>
9、时间等待相关问题。
例如:
 1 <span style="font-size:12px;">#include<iostream>
 2 #include<thread>
 3 #include<chrono>
 4 using namespace std;
 5 int main()
 6 {
 7     thread th1([]()
 8     {
 9         //让线程等待3秒
10         this_thread::sleep_for(chrono::seconds(3));
11         //让cpu执行其他空闲的线程
12         this_thread::yield();
13         //线程id
14         cout << this_thread::get_id() << endl;
15     });
16     return 0;
17 }</span>
10、线程功能拓展。
例如:
 1 <span style="font-size:12px;">#include<iostream>
 2 #include<thread>
 3 using namespace std;
 4 class MyThread :public thread   //继承thread
 5 {
 6 public:
 7     //子类MyThread()继承thread()的构造函数
 8     MyThread() : thread()
 9     {
10 
11     }
12     //MyThread()初始化构造函数
13     template<typename T, typename...Args>
14     MyThread(T&&func, Args&&...args) : thread(forward<T>(func), forward<Args>(args)...)
15     {
16     }
17     void showcmd(const char *str)  //运行system
18     {
19         system(str);
20     }
21 };
22 int main()
23 {
24     MyThread th1([]()
25     {
26         cout << "hello" << endl;
27     });
28     th1.showcmd("calc"); //运行calc
29     //lambda
30     MyThread th2([](const char * str)
31     {
32         cout << "hello" << str << endl;
33     }, " this is MyThread");
34     th2.showcmd("notepad");//运行notepad
35     return 0;
36 }
37 运行结果:
38 hello
39 //运行calc
40 hello this is MyThread
41 //运行notepad</span>
11、多线程可变参数。
例如:
 1 <span style="font-size:12px;">#include<iostream>
 2 #include<thread>
 3 #include<cstdarg>
 4 using namespace std;
 5 int show(const char *fun, ...)
 6 {
 7     va_list ap;//指针
 8     va_start(ap, fun);//开始
 9     vprintf(fun, ap);//调用
10     va_end(ap);
11         return 0;
12 }
13 int main()
14 {
15     thread t1(show, "%s    %d    %c    %f", "hello world!", 100, 'A', 3.14159);
16     return 0;
17 }
18 运行结果:
19 hello world!    100    A    3.14159</span>
12、线程交换。
例如:
 1 <span style="font-size:12px;">#include<iostream>
 2 #include<thread>
 3 using namespace std;
 4 int main()
 5 {
 6     thread t1([]()
 7     {
 8         cout << "thread1" << endl;
 9     });
10     thread t2([]()
11     {
12         cout << "thread2" << endl;
13     });
14     cout << "thread1' id is " << t1.get_id() << endl;
15     cout << "thread2' id is " << t2.get_id() << endl;
16     cout << "swap after:" << endl;
17     swap(t1, t2);//线程交换
18     cout << "thread1' id is " << t1.get_id() << endl;
19     cout << "thread2' id is " << t2.get_id() << endl;
20     return 0;
21 }
22 运行结果:
23 thread1
24 thread2
25 thread1' id is 4836
26 thread2' id is 4724
27 swap after:
28 thread1' id is 4724
29 thread2' id is 4836</span>
两个线程通过 swap 进行交换。
 
13、线程移动。
例如:
 1 <span style="font-size:12px;">#include<iostream>
 2 #include<thread>
 3 using namespace std;
 4 int main()
 5 {
 6     thread t1([]()
 7     {
 8         cout << "thread1" << endl;
 9     });
10     cout << "thread1' id is " << t1.get_id() << endl;
11     thread t2 = move(t1);;
12     cout << "thread2' id is " << t2.get_id() << endl;
13     return 0;
14 }
15 运行结果:
16 thread
17 thread1' id is 5620
18 thread2' id is 5620</span>

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
 
posted @ 2018-11-26 16:29  yimuxi  阅读(452)  评论(0编辑  收藏  举报