最近在多线程编程中多次使用到mutex来互斥,看了下项目的代码,有自己封装的mutex类,也有直接使用boost::mutex的,而boost中关于mutex可谓令人眼花撩换。这里总结一下。

对于mutex和lock,要明确一点,真正起到互斥作用的mutex,而lock可以认为是协助mutex令我们在使用时更方便。搞不清楚二者关系的可以参考这里:从高中一次半夜不冲厕所的经历谈程序

最基础的mutex的使用方法是这样的:

[html] view plaincopy

  1. HANDLE g_mutex = NULL;
  2. void test()
  3. {
  4. ::WaitForSingleObject(g_mutex, INFINITE);
  5. //do something...
  6. ReleaseMutex(g_mutex);
  7. }

使用匿名的互斥体,test函数中调用WaitForSingleObject等待其他线程释放g_mutex。INFINITE表示一直等待,也可以设置等待超时时间。

当前线程获得g_mutex后执行do something,之后释放g_mutex。

这里使用的是匿名互斥体,当然,在程序中需要多个互斥体时,可以通过CreateMutex创建命名互斥体,也可以通过OpenMutex打开一个互斥体。

不过很少有人像上面那样直接使用吧,太简单粗暴了,互斥体作为一个基本功能模块,怎么说也会封装一下,让别人用起来更爽些。如下是一个简单封装:

[html] view plaincopy

  1. class MyMutex
  2. {
  3. public:
  4. MyMutex()
  5. :m_hMutex(NULL)
  6. {
  7. }
  8. MyMutex(wchar_t* pMutexName)
  9. :m_hMutex(NULL)
  10. {
  11. createMutex(pMutexName);
  12. }
  13. virtual ~MyMutex()
  14. {
  15. destroyMutex();
  16. }
  17. bool lock()
  18. {
  19. return m_hMutex ? (::WaitForSingleObject(m_hMutex,INFINITE) == WAIT_OBJECT_0) : false;
  20. }
  21. void unlock()
  22. {
  23. ReleaseMutex(m_hMutex);
  24. }
  25. bool createMutex(wchar_t* pMutexName)
  26. {
  27. if (m_hMutex)
  28. {
  29. return true;
  30. }
  31. m_hMutex = ::CreateMutex(NULL, FALSE, pMutexName);
  32. return m_hMutex != NULL;
  33. }
  34. void destroyMutex()
  35. {
  36. CloseHandle(m_hMutex);
  37. m_hMutex = NULL;
  38. }
  39. bool openMutex(wchar_t* pMutexName)
  40. {
  41. if (m_hMutex)
  42. {
  43. return true;
  44. }
  45. m_hMutex = ::OpenMutex(SYNCHRONIZE, FALSE, pMutexName);
  46. return m_hMutex != NULL;
  47. }
  48. private:
  49. HANDLE m_hMutex;
  50. };

封装之后,就可以如下使用:

[html] view plaincopy

  1. void test1()
  2. {
  3. MyMutex mutex;
  4. mutex.createMutex(L"mutex_test_name1");
  5. if (mutex.lock())
  6. {
  7. //do something...
  8. mutex.unlock();
  9. }
  10. }

但是,就像从高中一次半夜不冲厕所的经历谈程序说的那样,释放锁就像”冲大便“一样让人容易忘记,如果在mutex.lock之后,忘记mutex.unlock,那么悲剧就发生了,当前线程将一直占据这把锁。。。。。

于是,便有了帮助类lock,或者叫做guard,或者lockguard等等吧。远离就是保证在离开作用域时,不需要手动调用unlock,而有人帮助我们做unlock的操作,省了很多事。一个帮助类lock大概是这样子的:

[html] view plaincopy

  1. class MyMutexLockGuard
  2. {
  3. public:
  4. MyMutexLockGuard(MyMutex* pMutex)
  5. {
  6. m_pMutex = pMutex;
  7. if (m_pMutex)
  8. {
  9. m_pMutex->lock();
  10. }
  11. }
  12. virtual ~MyMutexLockGuard()
  13. {
  14. if (m_pMutex)
  15. {
  16. m_pMutex->unlock();
  17. }
  18. }
  19. private:
  20. MyMutex* m_pMutex;
  21. };

调用起来大概是这样的:

[html] view plaincopy

  1. void test2()
  2. {
  3. MyMutex mutex(L"mutex_test_name2");
  4. {//scope 1
  5. MyMutexLockGuard lock(&mutex);
  6. //do something...
  7. }
  8. //out of the scope 1, the mutex has been unlocked
  9. }

当离开作用域1时,lock对象被析构,自动调用mutex.unlock函数释放锁。是不是很爽...

需要注意的是CreateMutex和OpenMutex这两个windows api,CreateMutex的第二个参数bInitialOwner最好设为false。设为true时代表创建这个mutex的线程是直接获取这个mutex,相当于创建这个mutex的过程中调用了waitforsingleobject,因此即使后来lock和unlock配对调用,最后先启动的这个线程还是没有释放这个mutex,必须手动再调用一次unlock才行。因此设为false更稳妥些。OpenMutex的第一个参数安全属性最好设为SYNCHRONIZE ,win7和vista下不要用ALL_ACCESS,有可能失败。第二参数表示进程创建出的子进程是否可以直接继承该mutex。

上面的是mutex的基本用法,更强大的是boost中对于mutex和lock的实现。

boost中的mutex貌似有6种或者更多,我用过的有3中boost::mutex、boost::shared_mutex、boost::recursive_mutex,貌似还有boost::try_mutex、boost::time_mutex,不过没用过。

boost::mutex是最基础的锁,有lock和unlock方法,可以认为是互持锁。boost::shared_mutex是共享锁,有lock、unlock方法以及shared_lock、shared_unlock方法。boost::recursive_mutex是重入锁或者称为递归锁,这个最后再说。

boost::shared_mutex可以用来实现读写锁。多线程中的一个经典问题是一写多读,即当有线程发生写操作时,所有其他线程的读操作暂停,其他时刻,允许多个线程同时读操作。使用boost::shared_mutex构造读写锁时需要使用到boost中的lock帮助类系列(作用类似上面我写的MyMutexLockGuard)。boost::shared_lock<T>和boost::unique_lock<T>,从字面上看,shared_lock是共享的,unique_lock是独占的,shared_lock<T>只允许T是shared_mutex,而unique_lock<T>对T没有限制,如果T是shared_mutex,则在执行加锁和解锁时会调用shared_lock和shared_unlock方法,否则,则执行一般的lock和unlock方法。

实现读写锁:

[html] view plaincopy

  1. typedef boost::unique_lock<boost::shared_mutex> ReadLock;
  2. typdef boost::shared_lock<boost::shared_mutex> WriteLock;
  3. boost::shared_mutex  read_write_mutex;
  4. void _ReadThreadFunc()
  5. {
  6. ReadLock read_lock(read_write_mutex);
  7. //read data...
  8. }
  9. void _WriteThreadFunc()
  10. {
  11. WriteLock write_lock(read_write_mutex);
  12. //write data...
  13. }

使用boost::unique_lock和boost::mutex则可以实现最基本的独占时互斥

[html] view plaincopy

  1. boost::unique<boost::mutex> MyLock
  2. boost::mutex myMutex;
  3. void func()
  4. {
  5. MyLock lock(myMutex);
  6. // do something...
  7. }

*注意:boost::mutex::scoped_lock和boost::unique是一个东东哦...

还有个东西叫boost::lock_guard,它是比boost::unique更轻量级的lock。看下boost::lock_guard的源代码:

[html] view plaincopy

  1. template<typename Mutex>
  2. class lock_guard
  3. {
  4. private:
  5. Mutex& m;
  6. explicit lock_guard(lock_guard&);
  7. lock_guard& operator=(lock_guard&);
  8. public:
  9. explicit lock_guard(Mutex& m_):
  10. m(m_)
  11. {
  12. m.lock();
  13. }
  14. lock_guard(Mutex& m_,adopt_lock_t):
  15. m(m_)
  16. {}
  17. ~lock_guard()
  18. {
  19. m.unlock();
  20. }
  21. };

可以看到只有两个public方法,即构造和析构函数,也就是说,使用boost::lock_guard去guard一个mutex,必然是在boost::lock_guard的对象离开其作用域时unlock它所guard的mutex,不提供提前unlock的功能。

而boost::unique_lock则提供这个功能,除了像boost::lock_guard一样在离开作用域时unlock它guard的mutex外,boost::unique还提供unlock函数,使用者可以手动执行unlock。此外,unique_lock还可以设置超时。

最后说下boost::recursive_mutex,先看下面的逻辑:main调用test3,test3中锁住g_mutex,调用test4,test4中尝试获得g_mutex,结果这个程序将死锁。因为test3锁住g_mutex后,在同一线程(更准确是同一堆栈中)再次尝试获得g_mutex,由于前面的锁未释放,这里将等待,形成死锁。

[html] view plaincopy

  1. boost::mutex g_mutex;
  2. void test4()
  3. {
  4. boost::lock_guard<boost::mutex> lock(g_mutex);
  5. //do something...
  6. }
  7. void test3()
  8. {
  9. boost::lock_guard<boost::mutex> lock(g_mutex);
  10. test4();
  11. //do something...
  12. }
  13. int _tmain(int argc, _TCHAR* argv[])
  14. {
  15. test3();
  16. return 0;
  17. }

第二个例子:

[html] view plaincopy

  1. void * thread_func_one(void *arg)
  2. {
  3. int i;
  4. for(i=0;i<10;i++){
  5. pthread_mutex_lock( &mutex1);
  6. pthread_mutex_lock( &mutex1);//锁两次
  7. count++;
  8. sleep(1);
  9. pthread_mutex_unlock(&mutex1);
  10. pthread_mutex_unlock(&mutex1);
  11. printf("thread one count value is %d/n",count);
  12. }
  13. return NULL;
  14. }

同样的道理,也死锁。那么对于这种在一个线程中可能在锁中需要再次获得锁的情况,就需要使用重入锁,boost::recursive_mutex。如下使用就没问题了。

[html] view plaincopy

  1. boost::recursive_mutex g_mutex;
  2. void test4()
  3. {
  4. boost::recursive_mutex::scoped_lock<boost::recursive_mutex > lock(g_mutex);
  5. //do something...
  6. }
  7. void test3()
  8. {
  9. boost::recursive_mutex::scoped_lock<boost::recursive_mutex > lock(g_mutex);
  10. test4();
  11. //do something...
  12. }
  13. int _tmain(int argc, _TCHAR* argv[])
  14. {
  15. test3();
  16. return 0;
  17. }

转载于:https://www.cnblogs.com/justinyo/archive/2013/03/21/2973688.html

boost中的mutex与lock相关推荐

  1. 【Boost】boost库中thread多线程详解2——mutex与lock

    1. mutex对象类 mutex类主要有两种:独占式与共享式的互斥量. ▲ 独占式互斥量: mutex: 独占式的互斥量,是最简单最常用的一种互斥量类型 try_mutex: 它是mutex的同义词 ...

  2. .NET 中使用 Mutex 进行跨越进程边界的同步 - walterlv

    .NET 中使用 Mutex 进行跨越进程边界的同步 - walterlv 原文:.NET 中使用 Mutex 进行跨越进程边界的同步 - walterlv .NET 中使用 Mutex 进行跨越进程 ...

  3. Multi-thread--C++11中std::mutex的使用

    C++11中新增了<mutex>,它是C++标准程序库中的一个头文件,定义了C++11标准中的一些互斥访问的类与方法等.其中std::mutex就是lock.unlock.std::loc ...

  4. golang goroutine实现_golang中的Mutex设计原理详解(一)

    Mutex系列是根据我对晁岳攀老师的<Go 并发编程实战课>的吸收和理解整理而成,如有偏差,欢迎指正~ 目标 本系列除了希望彻底学习和了解 golang 中 sync.Mutex 的原理和 ...

  5. C++11中std::mutex的使用

    原文转载于:https://blog.csdn.net/fengbingchun/article/details/73521630 C++11中新增了<mutex>,它是C++标准程序库中 ...

  6. Ubuntu中Could not get lock /var/lib/dpkg/lock解决方案

    关于Ubuntu中Could not get lock /var/lib/dpkg/lock解决方案 转载于:https://www.cnblogs.com/daemonFlY/p/10916812. ...

  7. 在WINDOWS SERVER 上或远程桌面中使用 MUTEX

    引用: http://www.cnblogs.com/fg0711/archive/2012/05/03/2480502.html 使用Mutex需要注意的两个细节 可能你已经注意到了,例子中在给Mu ...

  8. Boost中的Timer的使用——计算时间流逝

    使用Boost中的Timer库计算程序的运行时间 程序开发人员都会面临一个共同的问题,即写出高质量的代码完毕特定的功能.评价代码质量的一个重要标准就是算法的运行效率,也就是算法的运行时间.为了可靠的提 ...

  9. boost中bind的使用

    最近对boost的bind部分比较感兴趣,对其背后的机制进行了简单的分析,和大家分享一下. 注,我所看的代码是boost_1_64_0, 想来各个版本的差异不大. 定义函数 [cpp] view pl ...

最新文章

  1. 在Eclipse RCP中使用Spring AOP/ProxyFactory的问题
  2. php树形折叠三级菜单模板,三级树形竖向收缩列表菜单
  3. SpringMVC学习--文件上传
  4. php+编辑器+显示html,thinkphp——通过在线编辑器添加的内容在模板里正确显示(只显示内容,而不是html代码)...
  5. 雷电3和Type C区别
  6. tensorflow线下训练SSD深度学习物体检测模型,C++线上调用模型进行识别定位(干货满满)
  7. ORB-SLAM 解读(一)ORB关键点提取
  8. eclipse adt如何切换到设计界面_如何将你的UI界面设计的更精致?不仅仅是运用对比,还要注意细节 | 分享...
  9. Android航海航线地图,Alfa的航海大时代航线介绍 全航线跑商路线详解
  10. html中blockquote标签,html中blockquote标签的使用方法
  11. scoop bucket add 后仍然报错 Couldn‘t find manifest for
  12. Android通讯录管理(获取联系人、通话记录、短信消息)(一)
  13. 川希:精准引流的本质,被动涨粉的秘密。
  14. 【修真院java小课堂】什么是restful?rest的请求方法有哪些,有什么区别?
  15. 兼容所有浏览器的Web打印控件的设计方案
  16. texlive+texstudio数学建模排版
  17. k8s入坑之报错(9)k8s node节点加入到集群时卡住 “[preflight] Running pre-flight checks”...
  18. 学习HTTP协议——《图解HTTP》
  19. Java实现mysql保存微信特殊表情符号
  20. 创业公司股权分配的七大实操建议

热门文章

  1. Linux运维课程 第一阶段 重难点摘要(一)网络基础
  2. java中PriorityQueue优先级队列使用方法
  3. WCF Service Configuration Editor的使用
  4. 译Selenium Python Bindings 6 - WebDriver API
  5. 40个非常有创意的国外LOGO欣赏(上)
  6. 学习 ASP.NET MVC (第五回)理论篇
  7. Q96:PT(1):方格纹理(Checker)(1)——3D Checker
  8. 问题三十七:C++怎么解一元四次方程?(2)——怎么解一元三次方程
  9. 大数据分析有哪些技巧
  10. 大数据分析的作用与注意事项