zoukankan      html  css  js  c++  java
  • [转] 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
    ";
     8     n += 10;
     9     this_thread::sleep_for(chrono::milliseconds(10));
    10 }
    11 
    12 void fun2(int & n) //拷贝构造函数
    13 {
    14     cout << "Thread " << n << " executing
    ";
    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 << '
    ';
    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 << '
    ';
    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>
     
     
  • 相关阅读:
    Ubuntu:替换DASH图标
    使用 python 操作 mongodb 常用的操作
    boost Asio网络编程简介
    optional的使用
    boost中Function和Lambda的使用
    boost多线程入门介绍
    boost中bind的使用
    c++11新标准for循环和lambda表达式
    使用gcc命令编译多个文件
    编辑gif
  • 原文地址:https://www.cnblogs.com/yi-mu-xi/p/10021112.html
Copyright © 2011-2022 走看看