【c++】: c++11线程库
C++11线程库
C++11基于各个操作系统的线程操作进行了整合和面向对象封装,对于面向对象的语言来说,直接使用面向对象封装和接口显然对于开发者更加友好,因此,这里我根据我自己学习C++线程库的心得和体会以及线程库用法进行总结。
注:文中谈到的操作系统提供的系统接口都是Linux POSIX相关接口。
std::thread
std::thread实现的是针对线程的封装
- constructor: 构造函数直接可以创建一个线程执行对应的任务(函数)。
void task() { std::cout << "I am a thread" << std::endl; }
int main()
{std::thread t(task);if(t.joinable()) t.join();return 0;
}
细节
-
构造函数支持可变参数模版,我们可以在构造函数中提供函数的参数。
模拟实现
-
支持延迟定义,即首先定义一个std::thread进行默认初始化,后续通过匿名对象+移动构造直接定义。
int Plus(int left,int right) { return left + right; }
std::thread t;
t = std::thread(Plus,1,2);
- joinable : 是否join
- join : 如系统接口
- detach : 如系统接口
std::mutex
在Linux中实现的是对pthread_mutex_t的封装。
使用方式和pthread_mutex_t几乎一直,只是构造和析构有mutex的构造析构函数管理。
这里我们主要不是谈论std::mutex, 而是他的管理器std::unique_lock
std::unique_lock
我们知道C++11提出的通过std::unique_ptr, shared_ptr, weak_ptr对指针的声明周期进行管理,同时一定程度上解决内存泄露的问题。
那么指针的生命周期可以管理,那么锁的声明周期也可以管理,这里的unique_lock实现的就是通过RAII思想针对锁的声明周期进行管理。
- 最简单的方式,也是最常见的使用的方式。
std::mutex _mtx;
{std::unique_lock<std::mutex> lock(_mtx);
}
通过局部域的方式,实现进入局部域加锁,出局部域解锁,不会出现死锁。
- 更加精密的操作
我们可以看到unique_lock的构造函数还有一些操作,那么这些操作具体的作用是什么呢?
示例:
std::mutex _mtx;
std::unique_lock<std::mutex> lock(_mtx, std::try_to_lock);
- try_to_lock : 尝试加锁如果加锁失败,通过operator bool或者owns_lock判断是否加锁成功。
- defer_lock : 延迟定义,也就是先声明后面统一进行加锁。
std::mutex _mtx1 , _mtx2;
std::unique_lock lock1(_mtx1, std::defer_lock);
std::unique_lock lock2(_mtx2, std::defer_lock);
std::lock(lock1, lock2); // 延迟定义,统一进行加锁
- adopt_lock: 领养锁的声明周期,也就是锁已经加上了,这个时候我们可以直接领养这个锁并且管理他的生命周期
- 其他接口
比较简单,这里就不在进行过多的赘述。
std::condition_variable
针对条件变量的封装。
接口和Linux POSIX相关接口类似。
唯一值得注意的一点就是,wait的参数只能是std::unique_lock类型,不能直接传递原生锁或者std::mutex。
总结
这里总结介绍了C++11线程三板斧std::thread , std::mutex, std::condition_variable,帮助我们能够更加方便的实现线程操作,同时抹平了操作系统的差异,还是比较有用的。