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 却销毁了。
  • 相关阅读:
    升级windows 11小工具
    windows 10更新升级方法
    您需要了解的有关 Oracle 数据库修补的所有信息
    Step by Step Apply Rolling PSU Patch In Oracle Database 12c RAC Environment
    Upgrade Oracle Database Manually from 12.2.0.1 to 19c
    如何应用版本更新 12.2.0.1.210420(补丁 32507738 – 2021 年 4 月 RU)
    xtrabackup 安装、备份和恢复
    Centos_Lvm expand capacity without restarting CentOS
    Centos_Lvm_Create pv vg lv and mount
    通过全备+relaylog同步恢复被drop的库或表
  • 原文地址:https://www.cnblogs.com/mmc9527/p/10427924.html
Copyright © 2011-2022 走看看