2023年8月2日发(作者:)
BoostMutex详细解说博客参考:1. C++多线程编程的困扰C++从11开始在标准库之中引⼊了线程库来进⾏多线程编程,在之前的版本需要依托操作系统本⾝提供的线程库来进⾏多线程的编程。(其实本⾝就是在标准库之上对底层的操作系统多线程API统⼀进⾏了封装,使⽤的pthread或来进⾏多线程编程的)提供了统⼀的多线程固然是好事,但是标准库给的⽀持实在是有限,具体实践起来还是让⼈挺困扰的:C++本⾝的STL并不是线程安全的。所以缺少了类似与Java并发库所提供的⼀些⾼性能的线程安全的数据结构。(Doug Lea⼤神亲⾃操⼑完成的并发编程库,让JDK5成为Java之中⾥程碑式的版本)如果没有线程安全的数据结构,退⽽求其次,可以⾃⼰利⽤互斥量Mutex来实现。C++的标准库⽀持如下的互斥量的实现:互斥量mutextimed_mutex版本C++11作⽤最基本的互斥量有超时机C++11制的互斥量有超时机C++11制的互斥量C++11可重⼊的互斥量timed_mutexrecursive_mutexrecursive_timed_mutex结合 2,3C++11特点的互斥量具有超时机制的可C++14共享互斥量C++17共享的互斥量shared_timed_mutexshared_mutex由上述表格可见,C++是从14之后的版本才正式⽀持共享互斥量,也就是实现读写锁的结构。如果当前仅⽀持C++11的版本,所就没有办法使⽤共享互斥量来实现读写锁了。所以只能使⽤boost的库,利⽤boost提供的读写锁来完成所需完成的⼯作。2.标准库互斥量的剖析mutexmutex的中⽂翻译就是互斥量,很多⼈喜欢称之其为锁。其实不是太准确,因为多线程编程本质上应该通过互斥量之上加锁,解锁的操作,来实现多线程并发执⾏时对互斥资源线程安全的访问。 我们来看看mutex类的使⽤⽅法:long num = 0;std::mutex num_mutex;void numplus() { num_(); for (long i = 0; i < 1000000; ++i) { num++; } num_();};void numsub() { num_(); for (long i = 0; i < 1000000; ++i) { num--; } num_();}int main() { std::thread t1(numplus); std::thread t2(numsub); (); (); std::cout << num << std::endl;}调⽤线程从成功调⽤lock()或try_lock()开始,到unlock()为⽌占有mutex对象。当存在某线程占有mutex时,所有其他线程若调⽤lock则会阻塞,⽽调⽤try_lock会得到false返回值。由上述代码可以看到,通过mutex加锁的⽅式,来确保只有单⼀线程对临界区的资源进⾏操作。time_mutex与recursive_mutex的使⽤也是⼤同⼩异,两者都是基于mutex来实现的。( 本质上是基于recursive_mutex实现的,mutex为recursive_mutex的特例)time_mutex则是进⾏加锁时可以设置阻塞的时间,若超过对应时长,则返回false。recursive_mutex则让单⼀线程可以多次对同⼀互斥量加锁,同样,解锁时也需要释放相同多次的锁。以上三种类型的互斥量都是包装了操作系统底层的pthread_mutex_t:在C++之中并不提倡我们直接对锁进⾏操作,因为在lock之后忘记调⽤unlock很容易造成死锁。⽽对临界资源进⾏操作时,可能会抛出异常,程序也有可能break,return 甚⾄ goto,这些情况都极容易导致unlock没有被调⽤。所以C++之中通过RAII来解决这个问题,它提供了⼀系列的通⽤管理互斥量的类:互斥量管理版本作⽤lock_graudC++11基于作⽤域的互斥量管理unique_lockC++11更加灵活的互斥量管理shared_lockC++14共享互斥量的管理scope_lockC++17多互斥量避免死锁的管理创建互斥量管理对象时,它试图给给定mutex加锁。当程序离开互斥量管理对象的作⽤域时,互斥量管理对象会析构并且并释放mutex。所以我们则不需要担⼼程序跳出或产⽣异常引发的死锁了。对于需要加锁的代码段,可以通过{}括起来形成⼀个作⽤域。⽐如上述代码的栗⼦,可以进⾏如下改写(推荐):long num = 0;std::mutex num_mutex;void numplus() { std::lock_guard lock_guard(num_mutex); for (long i = 0; i < 1000000; ++i) { num++; }};void numsub() { std::lock_guard lock_guard(num_mutex); for (long i = 0; i < 1000000; ++i) { num--; }}int main() { std::thread t1(numplus); std::thread t2(numsub); (); (); std::cout << num << std::endl;}由上述代码可以看到,代码结构变得更加明晰了,对于锁的管理也交给了程序本⾝来进⾏处理,减少了出错的可能。shared_mutexC++14的版本之后提供了共享互斥量,它的区别就在于提供更加细粒度的加锁操作:lock_shared。lock_shared是⼀个获取共享锁的操作,⽽lock是⼀个获取排他锁的操作,通过这种⽅式更加细粒度化锁的操作。shared_mutex也是基于操作系统底层的读写锁pthread_rwlock_t的封装:这⾥有个事情挺奇怪的,C++14提供了shared_timed_mutex ⽽在C++17提供了shared_mutex。其实shared_timed_mutex涵盖了shard_mutex的功能。(不知道是不是因为名字被diss了,所以后续在C++17⾥将shared_mutex**加了回来)。共享互斥量适⽤与读多写少的场景,举个栗⼦:long num = 0;std::shared_mutex num_mutex;// 仅有单个线程可以写num的值。void numplus() { std::unique_lock lock_guard(num_mutex); for (long i = 0; i < 1000000; ++i) { num++; }};// 多个线程同时读num的值。long numprint() { std::shared_lock lock_guard(num_mutex); return num;}简单来说:shared_lock是读锁。被锁后仍允许其他线程执⾏同样被shared_lock的代码unique_lock是写锁。被锁后不允许其他线程执⾏被shared_lock或unique_lock的代码。它可以同时限制unique_lock与share_lock#include #include "boost/thread/"#include "boost/thread/"typedef boost::shared_lock readLock;typedef boost::unique_lock writeLock;boost::shared_mutex rwmutex;std::vector shared_vec = { 1, 2, 3, 4, 5, 6 };int pause = 0;void wait(int milliseconds){ boost::this_thread::sleep(boost::posix_time::milliseconds(milliseconds));}void readThread1(){ while (true) { wait(100); if (pause) { continue; } readLock rdlock(rwmutex); printf("rthread 1: vec[1]: %dn", shared_vec[1]); }}void readThread2(){ while (true) { wait(100); if (pause) { continue; } readLock rdlock(rwmutex); printf("rthread 2: vec[2]: %dn", shared_vec[2]); }}void writeThread(){ int count = 1; while (true) { count++; wait(500); if (pause) { continue; }
writeLock wdlock(rwmutex); shared_vec[1] = count; shared_vec[2] = count + 1; printf("wthread 2: vec[1] and vec[2]: %d %dn", shared_vec[1], shared_vec[2]); if (count >= 100) { count = 0; } }}int main(){ boost::thread t1(readThread1); wait(20); boost::thread t2(readThread2); wait(20); boost::thread t3(writeThread); while (true) { char c = getchar(); if (c == 's') { pause = 1; } else if (c == 'r') { pause = 0; } else if (c == 'q') { break; } wait(500); } return 0;}不得不说,C++11没有将共享互斥量集成进来,在很多读多写少的应⽤场合之中,标准库本⾝提供的锁机制显得很鸡肋,也从⽽导致了笔者最终只能求助与boost的解决⽅案。多锁竞争还剩下最后⼀个要写的内容:scope_lock ,当我们要进⾏多个锁管理时,很容易出现问题,由于加锁的先后顺序不同导致死锁。(其实本来不想写了,好累。这⾥就简单⽤例⼦做解释吧,偷个懒~~)如下栗⼦,加锁顺序不当导致死锁:std::mutex m1, m2;// thread 1{ std::lock_guard lock1(m1); std::lock_guard lock2(m2);}// thread 2{ std::lock_guard lock2(m2); std::lock_guard lock1(m1);}⽽通过C++17提供的scope_lock就可以很简单解决这个问题了:std::mutex m1, m2;// thread 1{ std::scope_lock lock(m1, m2);}// thread 2{ std::scope_lock lock(m1, m2);}boost::timed_mutex#include #include "boost/thread/"#include "boost/thread/"#include "ComUtils/TimeStampAbs.h"void wait(int seconds){ boost::this_thread::sleep(boost::posix_time::seconds(seconds));}boost::timed_mutex mutex;void timed_mutex_func1(){ for (int i = 0; i < 5; ++i) { double t1 = TimeStampAbs::getTimeStamp(TIME_STAMP_TYPE::ABS_TIME_STAMP); boost::unique_lock lock(mutex, boost::get_system_time() + boost::posix_time::milliseconds(100)); if (!_lock()) { printf("thread 1 owns_lock failedn"); continue; } else { printf("thread 1 owns_lock successn"); double t2 = TimeStampAbs::getTimeStamp(TIME_STAMP_TYPE::ABS_TIME_STAMP); printf("thread 1 dt: %lf %lf %lfn", t1, t2, t2 - t1); } wait(1); std::cout << "Thread 1 " << boost::this_thread::get_id() << ": " << i < lock(mutex, boost::get_system_time() + boost::posix_time::milliseconds(1500)); if (!_lock()) { printf("thread 2 owns_lock failedn"); continue; } else { printf("thread 2 owns_lock successn"); double t2 = TimeStampAbs::getTimeStamp(TIME_STAMP_TYPE::ABS_TIME_STAMP); printf("thread 2 dt: %lf %lf %lfn", t1, t2, t2 - t1); } boost::thread::sleep(boost::get_system_time() + boost::posix_time::milliseconds(40)); std::cout << "Thread 2 " << boost::this_thread::get_id() << ": " << i << std::endl; }}int main(){ boost::thread t(timed_mutex_func1); boost::thread t2(timed_mutex_func2); getchar(); return 0;}简单来说boost::timed_mutex就是进⾏加锁时可以设置阻塞的时间,若超过对应时长,则返回false;如上述线程1最长等待时间 100ms, 线程2最长等待时间是 1500ms;此案例细节可参考:
发布者:admin,转转请注明出处:http://www.yc00.com/web/1690906238a460123.html
评论列表(0条)