主页 > 软件开发  > 

19.6、C++11新特性有哪些⑥【并发】

19.6、C++11新特性有哪些⑥【并发】

C++11新特性有哪些 并发线程(thread)原子类型互斥量(Mutex)锁(lock)

并发 线程(thread)

创建线程对象后,必须要提供线程关联函数。两者都聚齐了,才能启动线程。线程函数一般情况下可按照以下四种方式提供: 函数指针lambda表达式函数对象包装器(function) void ThreadFunc(int a) { cout << "Thread1" << a << endl; } void T() { cout << "hello" << endl; } class TF { public: void TT() { cout << "NI" << endl; } void operator()() { cout << "Thread3" << endl; } }; int main() { // 线程函数为函数指针 thread t1(ThreadFunc, 10); // 线程函数为lambda表达式 thread t2([] {cout << "Thread2" << endl; }); // 线程函数为函数对象 TF tf; thread t3(tf); thread t5(&TF::TT, TF()); //线程函数为包装器 function<void()> t = T; thread t4(t); t1.join(); t2.join(); t3.join(); t4.join(); t5.join(); cout << "Main thread!" << endl; return 0; }

禁止拷贝线程对象:thread类 不允许拷贝构造以及赋值,但是可以移动构造和移动赋值,即一个线程对象关联的 线程的状态 转移给 其他线程对象,转移期间不影响线程的执行

线程函数参数

线程函数的参数是以 值拷贝 的方式 拷贝到线程栈空间中的,实际引用的是 线程栈中的拷贝,而不是外部实参。所以,即使参数是引用类型,最后也不能把改变后的结果带到外面。注意:如果线程参数是类成员函数时,必须将this作为线程函数参数

可以通过 joinable()函数 判断线程是否是有效的,如果是以下任意情况,则线程无效:

线程对象没有关联函数线程对象的状态已经转移给其他线程对象线程已经调用join 或者detach结束 原子类型 声明一个类型为T 的原子类型变量t: atmoic<T> t; // 声明一个类型为T的原子类型变量t

特点:

如果是对原子类型的变量进行修改,就不需要加锁了。线程会自动地互斥访问原子类型

原子类型是不允许拷贝和赋值的,因为atmoic模板类中的拷贝构造、移动构造、赋值运算符重载都被删除掉了

#include <atomic> int main() { atomic<int> a1(0); //atomic<int> a2(a1); // 编译失败 atomic<int> a2(0); //a2 = a1; // 编译失败 return 0; }

实例:

该例中,使用原子变量后 就不再需要加锁了

互斥量(Mutex)

原子类型可以保证一个变量的安全性,但是对于一段代码的安全性,我们只能通过 互斥量和锁 来实现

普通互斥量(std:: mutex)

注意,线程函数调用 lock() 时,可能会发生以下三种情况:

如果该互斥量当前没有被锁住,则调用线程将该互斥量锁住,直到调用 unlock之前,该线程一直拥有该锁如果当前互斥量被其他线程锁住,则当前的调用线程被阻塞住如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)

线程函数调用 try_lock() 时,可能会发生以下三种情况:

如果当前互斥量没有被其他线程占有,则该线程锁住互斥量,直到该线程调用 unlock释放互斥量如果当前互斥量被其他线程锁住,则当前调用线程返回 false,而并不会被阻塞掉如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)

递归互斥量(std:: recursive_mutex)

递归互斥锁 允许同一个线程对互斥量多次上锁(即递归上锁),来获得对互斥量对象的多层所有权。释放互斥量时需要调用相同次数的 unlock()。除此之外,std::recursive_mutex 的特性和 std::mutex 大致相同

这对于递归函数 可能需要在同一线程中多次获取锁的情况很有用:

#include <iostream> #include <mutex> #include <thread> std::recursive_mutex myRecursiveMutex; void recursiveAccess(int depth) { std::unique_lock<std::recursive_mutex> lock(myRecursiveMutex); if (depth > 0) { recursiveAccess(depth - 1); } // 访问共享资源的代码 std::cout << "Accessing shared resource at depth " << depth << "...\n"; } int main() { std::thread t1(recursiveAccess, 3); t1.join(); return 0; }

定时互斥量(std::timed_mutex)

比 std::mutex 多了两个成员函数,try_lock_for(),try_lock_until() :

try_lock_for():函数参数表示一个时间范围,在这一段时间范围之内线程如果没有获得锁 则保持阻塞;如果在此期间其他线程释放了锁,则该线程可获得该互斥锁;如果超时(指定时间范围内没有获得锁),则函数调用返回false。 timed_mutex myMutex; chrono::milliseconds timeout(100); //100毫秒 if (myMutex.try_lock_for(timeout)) { //在100毫秒内获取了锁 //业务代码 myMutex.unlock(); //释放锁 } else { //在100毫秒内没有获取锁 //业务代码 }

try_lock_until():函数参数表示一个时刻,在这一时刻之前线程如果没有获得锁则保持阻塞;如果在此时刻前其他线程释放了锁,则该线程可获得该互斥锁;如果超过指定时刻没有获得锁,则函数调用返回false。

定时递归互斥量(std::recursive_timed_mutex)

允许同一线程多次获取锁,并提供了超时功能。与std::timed_mutex一样,std::recursive_timed_mutex也提供了try_lock_for()和try_lock_until()方法

锁(lock)

这里主要介绍两种RAII方式的锁封装(std::lock_guard和std::unique_lock),可以动态的释放锁资源,防止线程由于编码失误导致一直持有锁。

std::lock_gurad 是 C++11 中定义的模板类,取代了mutex的lock和unlock函数。定义如下:

lock_guard类模板通过RAII来封装。在需要加锁的地方,只需要用互斥量实例化一个lock_guard对象,此时类内调用构造函数成功上锁;出作用域前,lock_guard对象要被销毁,调用析构函数自动解锁,可以有效避免死锁问题。lock_guard对象之间是不能拷贝和赋值的lock_guard的缺陷:太单一,用户没有办法对该锁进行控制,因此C++11又提供了unique_lock。

独占锁(unique_lock)

与lock_guard不同的是,unique_lock更加的灵活(但效率上差一点,内存占用多一点),提供了更多的成员函数:

上锁/解锁操作:lock、try_lock、try_lock_for、try_lock_until和unlock //1.立即上锁 std::mutex mtx; std::unique_lock<std::mutex> lck(mtx); //2.延迟上锁 std::unique_lock<std::mutex> lck(mtx, std::defer_lock); //只是创建对象,不上锁 lck.lock(); // 此时才上锁 if(lck.try_lock()){ // 已获得锁 } lck.unlock(); 修改操作:移动赋值、交换即swap(与另一个unique_lock对象互换所管理的互斥量所有权)、释放即release(返回它所管理的互斥量对象的指针,并释放所有权) std::unique_lock<std::mutex> ul1(mtx); std::unique_lock<std::mutex> ul2 = std::move(ul1); // 把ul1的锁转移到ul2上,即现在是ul1对mtx上锁 if (!ul1.owns_lock()) // 现在 ul1 应该不再持有锁了 { assert(true); } 获取属性:owns_lock(返回当前对象是否上了锁)、operator bool()(与owns_lock()的功能相同)、mutex(返回当前unique_lock所管理的互斥量的指针) if(lck.owns_lock()){ // 此时拥有锁 } auto* m = lck.mutex();
标签:

19.6、C++11新特性有哪些⑥【并发】由讯客互联软件开发栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“19.6、C++11新特性有哪些⑥【并发】