zoukankan      html  css  js  c++  java
  • C++11并发之std::thread

    知识链接:
    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 各种构造函数例子如下:
    [cpp] view plain copy
     
    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 ";  
    8.     n += 10;  
    9.     this_thread::sleep_for(chrono::milliseconds(10));  
    10. }  
    11. void fun2(int & n) //拷贝构造函数  
    12. {  
    13.     cout << "Thread " << n << " executing ";  
    14.     n += 20;  
    15.     this_thread::sleep_for(chrono::milliseconds(10));  
    16. }  
    17. int main()  
    18. {  
    19.     int n = 0;  
    20.     thread t1;               //t1不是一个thread  
    21.     thread t2(fun1, n + 1);  //按照值传递  
    22.     t2.join();  
    23.     cout << "n=" << n << ' ';  
    24.     n = 10;  
    25.     thread t3(fun2, ref(n)); //引用  
    26.     thread t4(move(t3));     //t4执行t3,t3不是thread  
    27.     t4.join();  
    28.     cout << "n=" << n << ' ';  
    29.     return 0;  
    30. }  
    31. 运行结果:  
    32. Thread 1 executing  
    33. n=0  
    34. Thread 10 executing  
    35. n=30</span>  
     
    3、异步。
    例如:
    [cpp] view plain copy
     
    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、多线程传递参数。
    例如:
    [cpp] view plain copy
     
    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例子如下:
    [cpp] view plain copy
     
    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例子如下:
    [cpp] view plain copy
     
    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核心个数。
    例如:
    [cpp] view plain copy
     
    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原子变量与线程安全。
    问题例如:
    [cpp] view plain copy
     
    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)互斥量。
    例如:
    [cpp] view plain copy
     
    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. int main()  
    18. {  
    19.     clock_t start = clock();  
    20.     thread t1(run);  
    21.     thread t2(run);  
    22.     t1.join();  
    23.     t2.join();  
    24.     clock_t end = clock();  
    25.     cout << "num=" << num << ",用时 " << end - start << " ms" << endl;  
    26.     return 0;  
    27. }  
    28. 运行结果:  
    29. num=200000000,用时 128323 ms</span>  
    不难发现,通过互斥量后运算结果正确,但是计算速度很慢,原因主要是互斥量加解锁需要时间。
    互斥量详细内容 请参考C++11 并发之std::mutex
    (2)原子变量。
    例如:
    [cpp] view plain copy
     
    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. 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.     thread t2(run);  
    19.     t1.join();  
    20.     t2.join();  
    21.     clock_t end = clock();  
    22.     cout << "num=" << num << ",用时 " << end - start << " ms" << endl;  
    23.     return 0;  
    24. }  
    25. 运行结果:  
    26. num=200000000,用时 29732 ms</span>  
    不难发现,通过原子变量后运算结果正确,计算速度一般。
    原子变量详细内容 请参考C++11 并发之std::atomic。
    (3)加入 join 。
    例如:
    [cpp] view plain copy
     
    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.     t1.join();  
    18.     thread t2(run);  
    19.     t2.join();  
    20.     clock_t end = clock();  
    21.     cout << "num=" << num << ",用时 " << end - start << " ms" << endl;  
    22.     return 0;  
    23. }  
    24. 运行结果:  
    25. num=200000000,用时 626 ms</span>  
    不难发现,通过原子变量后运算结果正确,计算速度也很理想。
     
    8、lambda与多线程。
    例如:
    [cpp] view plain copy
     
    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、时间等待相关问题。
    例如:
    [cpp] view plain copy
     
    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、线程功能拓展。
    例如:
    [cpp] view plain copy
     
    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.     //MyThread()初始化构造函数  
    12.     template<typename T, typename...Args>  
    13.     MyThread(T&&func, Args&&...args) : thread(forward<T>(func), forward<Args>(args)...)  
    14.     {  
    15.     }  
    16.     void showcmd(const char *str)  //运行system  
    17.     {  
    18.         system(str);  
    19.     }  
    20. };  
    21. int main()  
    22. {  
    23.     MyThread th1([]()  
    24.     {  
    25.         cout << "hello" << endl;  
    26.     });  
    27.     th1.showcmd("calc"); //运行calc  
    28.     //lambda  
    29.     MyThread th2([](const char * str)  
    30.     {  
    31.         cout << "hello" << str << endl;  
    32.     }, " this is MyThread");  
    33.     th2.showcmd("notepad");//运行notepad  
    34.     return 0;  
    35. }  
    36. 运行结果:  
    37. hello  
    38. //运行calc  
    39. hello this is MyThread  
    40. //运行notepad</span>  
     
    11、多线程可变参数。
    例如:
    [cpp] view plain copy
     
    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、线程交换。
    例如:
    [cpp] view plain copy
     
    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.       
    17.     cout << "swap after:" << endl;  
    18.     swap(t1, t2);//线程交换  
    19.     cout << "thread1' id is " << t1.get_id() << endl;  
    20.     cout << "thread2' id is " << t2.get_id() << endl;  
    21.     return 0;  
    22. }  
    23. 运行结果:  
    24. thread1  
    25. thread2  
    26. thread1' id is 4836  
    27. thread2' id is 4724  
    28. swap after:  
    29. thread1' id is 4724  
    30. thread2' id is 4836</span>  
    两个线程通过 swap 进行交换。
     
    13、线程移动。
    例如:
    [cpp] view plain copy
     
    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. thread1  
    17. thread1' id is 5620  
    18. thread2' id is 5620</span>  
    从上述代码中,线程t2可以通过 move 移动 t1 来获取 t1 的全部属性,而 t1 却销毁了。
  • 相关阅读:
    快速搞懂.NET 5/.NET Core应用程序的发布部署
    .NET 5 程序高级调试-WinDbg
    mmap出现 Permission denied
    Java int和integer有什么区别 (mybatis踩坑)
    NodeJS mysql timestamp 数据插入失败的问题
    mysql case when 用法
    postcss 源码解析以及运用
    rust漫游
    关于接口设计的思考--我们真的需要这么多入参吗
    详解apollo的设计与使用
  • 原文地址:https://www.cnblogs.com/lidabo/p/7852033.html
Copyright © 2011-2022 走看看