在C++11以前,C++的多线程编程均需依赖系统或第三方接口实现,一定程度上影响了代码的移植性。C++11中,引入了boost库中的多线程部分内容,形成C++标准,形成标准后的boost多线程编程部分接口基本没有变化,这样方便了以前使用boost接口开发的使用者切换使用C++标准接口,很容易把boost接口升级为C++标准接口。
我们通过如下几部分介绍C++11多线程方面的接口及使用方法。
1. std::thread
std::thread为C++11的线程类,使用方法和boost接口一样,非常方便,同时,C++11的std::thread解决了boost::thread中构成参数限制的问题,我想这都是得益于C++11的可变参数的设计风格。
我们通过如下代码熟悉下std::thread使用风格:
1//c11.cpp
2#include <iostream>
3#include <thread>
4void threadfun1()
5{
6 std::cout << "threadfun1 - 1\r\n" << std::endl;
7 std::this_thread::sleep_for(std::chrono::seconds(1));
8 std::cout << "threadfun1 - 2" << std::endl;
9}
10void threadfun2(int iParam, std::string sParam)
11{
12 std::cout << "threadfun2 - 1" << std::endl;
13 std::this_thread::sleep_for(std::chrono::seconds(5));
14 std::cout << "threadfun2 - 2" << std::endl;
15}
16int main()
17{
18 std::thread t1(threadfun1);
19 std::thread t2(threadfun2, 10, "abc");
20 t1.join();
21 std::cout << "join" << std::endl;
22 t2.detach();
23 std::cout << "detach" << std::endl;
24}
注意编译时要使用:g++ c11.cpp -lpthread
运行结果:
1threadfun1 - 1
2threadfun2 - 1
3threadfun1 - 2
4join
5detach
2. std::atomic
std::atomic为C++11封装的原子数据类型。
什么是原子数据类型?从功能上看,简单地说,原子数据类型不会发生数据竞争,能直接用在多线程中而不必我们用户对其进行添加互斥资源锁的类型。从实现上来看,我们可以理解为这些原子类型内部自己加了锁。
我们下面通过一个测试例子说明原子类型std::atomic
的特点。
我们使用10个线程,把std::atomic
类型的变量iCount从10减到1。
1//c11.cpp
2#include <thread>
3#include <atomic>
4#include <stdio.h>
5#include <iostream>
6#include <list>
7std::atomic<bool> bIsReady(false);
8std::atomic<int> iCount(10);
9void threadfun1()
10{
11 if (!bIsReady) {
12 std::this_thread::yield();
13 }
14 while (iCount > 0)
15 {
16 printf("iCount:%d\r\n", iCount--);
17 }
18}
19int main()
20{
21 std::list<std::thread> lstThread;
22 for (int i = 0; i < 10; ++i)
23 {
24 lstThread.push_back(std::thread(threadfun1));
25 }
26 for (auto& th : lstThread)
27 {
28 th.join();
29 }
30}
运行结果:
1iCount:10
2iCount:9
3iCount:8
4iCount:7
5iCount:6
6iCount:5
7iCount:4
8iCount:3
9iCount:2
10iCount:1
从上面的结果可以看到,iCount的最小结果是1,没有出现小于等于0的情况,大家可以把iCount改成100甚至1000看看,可能会更直观一点。
3. std::condition_variable
C++11中的std::condition_variable就像Linux下使用pthread_cond_wait和pthread_cond_signal一样,可以让线程休眠,直到被唤醒,然后再重新执行。线程等待在多线程编程中使用非常频繁,经常需要等待一些异步执行的条件的返回结果。
代码如下:
1#include <iostream> // std::cout
2#include <thread> // std::thread
3#include <mutex> // std::mutex, std::unique\_lock
4#include <condition\_variable> // std::condition\_variable
5std::mutex mtx;
6std::condition_variable cv;
7bool ready = false;
8void print\_id(int id) {
9 std::unique_lock<std::mutex> lck(mtx);
10 while (!ready) cv.wait(lck); //线程将进入休眠
11 // ...
12 std::cout << "thread " << id << '\n';
13}
14void go() {
15 std::unique_lock<std::mutex> lck(mtx);
16 ready = true;
17 cv.notify_all();
18}
19int main()
20{
21 std::thread threads[10];
22 // spawn 10 threads:
23 for (int i = 0; i<10; ++i)
24 threads[i] = std::thread(print_id, i);
25 std::cout << "10 threads ready to race...\n";
26 go(); // go!
27 for (auto& th : threads) th.join();
28 return 0;
29}
运行结果:
110 threads ready to race...
2thread 0
3thread 1
4thread 2
5thread 3
6thread 4
7thread 5
8thread 6
9thread 7
10thread 8
11thread 9
上面的代码,在调用go函数之前,10个线程都处于休眠状态,当cv.notify_all()运行后,线程休眠结束,继续往下运行,最终输出如上结果。