C++11新特性之线程操作
C++11之前没有对并发编程提供语言级别的支持,这使得我们在编写可移植的并发程序时,存在诸多的不便。现在C++11增加了线程以及线程相关的类,很方便地支持了并发编程,使得编写的多线程程序的可移植性得到了很大的提高。
1. 线程
1.1 线程的创建
#inclde <thread>
std::thead t(ThreadFun, parm1, parm2,...);
t.join();或t.detach();
join会阻塞线程,直到线程函数结束
detach让线程和线程对象分离,让线程作为后台线程去执行,但需要注意,detach之后就无法再和线程发生联系了,等于说失去了对线程的控制权。
2. 互斥量
C++11提供了以下4中语义的互斥量:
std::mutex:独占互斥量,不能递归使用
std::timed_mutex:带超时的独占互斥量,不能递归使用
std::recursive_mutex:递归互斥量,不带超时功能
std::recursive_timed_mutex:带超时的递归互斥量
2.1 独占互斥量
std::mutex m_mutex;
mutex.lock();
do something;
mutex.unlock();
注意:使用std::lock_guard<mutex> locker(m_mutex);可以简化lock/unlock的写法,同时也更安全,因为lock_guard在构造的时候会自动锁定互斥量,而在退出作用域后进行析构时就会自动解锁,从而保证了互斥量的正确操作。
try_lock()尝试锁定互斥量,如果成功则返回true
2.2 递归的独占互斥量
需要注意的是尽量不要使用递归锁:
(1)需要用到递归锁的多线程互斥处理本身就应该可以简化的,运行递归互斥很容易放纵复杂逻辑的产生
(2)递归锁比起非递归锁要麻烦,效率低
(3)递归锁虽然允许同一个线程多次获得同一个互斥量,可重复获得的最大次数并未具体说明,一旦超过一定次数会抛出std::system错误
2.3 带超时的互斥量和递归带超时的互斥量
std::timed_mutex比std::mutex多了两个超时获取锁的接口,try_lock_for和try_lock_until,这两个接口是用开设置获取互斥量的超时时间,使用时可以用一个while循环去不断地获取互斥量。
std::timed_mutex mutex;
std::chrono::secord timeout(2);
if (mutex.try_lock_for(timeout))
cout << "do work with the mutex" << endl;
else:
cout << "do work without the mutex" << endl;
3. 条件变量
3.1 说明
条件变量用于线程的同步机制,它能阻塞一个或多个线程,直到收到另外一个线程发出的同质或者超时,才会唤醒当前阻塞的线程。条件变量需要和互斥变量结合起来用。
C++提供了两种条件变量:
(1)condition_variable,配合std::unique_lock<std::mutex>进行wait操作
(2)condition_variable_any,和任意带有lock,unlock语义的mutex搭配使用,比较灵活,但效率比condition_variable低
注意以下函数的使用:
(1)std::lock_guard,它利用了RAII机制可以保证mutex的安全释放
(2)std::unique_lock与lock_guard的区别在与,前者可以自由地释放mutex,而后者需要等到std::lock_guard变量生命周期结束时才能释放。
3.2 示例实现消息循环队列
3.2.1 实现代码
// 使用C++11的新特性实现线程安全的循环消息队列 #pragma once #include<iostream> #include<mutex> #include<condition_variable> using namespace std; #define MAXQUEUELEN 32 template<typename T> class CMsgQueue { public: CMsgQueue() { m_pQueue = new T[MAXQUEUELEN]; m_nHead = m_nTail = 0; } ~CMsgQueue() { Clear(); } void Push(T msg) { unique_lock<mutex> lock(m_Mutex); while (IsFull()) { cout << "消息队列已经满,请等待..." << endl; m_ConditionVar.wait(lock); } cout << "Push: " << msg << endl; m_pQueue[m_nTail] = msg; m_nTail = m_nTail % MAXQUEUELEN + 1; m_ConditionVar.notify_all(); } bool IsFull() { return (m_nTail + 1) % MAXQUEUELEN == m_nHead; } bool IsEmpty() { return m_nTail == m_nHead; } T Pop() { unique_lock<mutex> lock(m_Mutex); while (IsEmpty()) { cout << "消息队列为空,请等待..." << endl; m_ConditionVar.wait(lock); } T msg = m_pQueue[m_nHead]; cout << "Pop: " << msg << endl; m_nHead = m_nHead % MAXQUEUELEN + 1; m_ConditionVar.notify_all(); return msg; } void Clear() { if (m_pQueue != NULL) { delete[] m_pQueue; m_pQueue = NULL; } } private: T *m_pQueue; int m_nHead; int m_nTail; mutex m_Mutex; condition_variable m_ConditionVar; };
3.2.2 测试
void fun1(CMsgQueue<int> *pQueue) { for (int i = 0; i < 40; i++) { //this_thread::sleep_for(chrono::seconds(2)); pQueue->Push(i); } } void fun2(CMsgQueue<int> *pQueue) { for (int i = 0; i < 20; i++) { this_thread::sleep_for(chrono::seconds(2)); pQueue->Pop(); } } void main() { CMsgQueue<int> *pQueue = new CMsgQueue<int>; thread t1(fun1, pQueue); thread t2(fun2, pQueue); t1.join(); t2.join(); return; }