ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

C++多线程:互斥锁

C++多线程:互斥锁 1.前言比如说我们现在以一个list容器来模仿一个消息队列当消息来临时插入list的尾部当读取消息时就把头部的消息读出来并且删除这条消息。在代码中就以两个线程分别实现消息写入和消息读取的功能如下class msgList { private: listintmylist; //用list模仿一个消息队列 public: void WriteList() //向消息队列中写入消息(以i作为消息) { for (int i 0; i100000; i) { cout Write : i endl; mylist.push_back(i); } return; } void ReadList() //从消息队列中读取并取出消息 { for(int i0;i100000;i) { if (!mylist.empty()) { cout Read : mylist.front() endl; mylist.pop_front(); } else { cout Message List is empty! endl; } } } }; int main() { msgList mlist; thread pread(msgList::ReadList, mlist); //读线程 thread pwrite(msgList::WriteList, mlist); //写线程 //等待线程结束 pread.join(); pwrite.join(); return 0; }这段程序在运行过程中大部分时间是正常的但是也会出现如下不稳定的情况为什么会出现这种情况呢这是因为消息队列对于读线程和写线程来说是共享的这时就会出现两种特殊的情况读线程的读取操作还没有结束线程上下文就切换到了写线程中或者写线程的写入操作还没有结束线程上下文切换就到了读线程中这两种情况都反映了读写冲突从而出现了以上错误。要想解决这个问题最显然最直接的方法就是将读写操作分离开来读的时候不允许写写的时候不允许读这样才能实现线程安全的读和写。说形象一点就是在进行读操作时就对共享资源进行加锁禁止其他线程访问其他线程要访问就得等到读线程解锁才行就像上厕所一样一次只能上一个人其他人必须得等他上完了再上。这样就有了互斥锁的概念。2 互斥锁在多任务操作系统中同时运行的多个任务可能都需要使用同一种资源。比如说同一个文件可能一个线程会对其进行写操作而另一个线程需要对这个文件进行读操作可想而知如果写线程还没有写结束而此时读线程开始了或者读线程还没有读结束而写线程开始了那么最终的结果显然会是混乱的。为了保护共享资源在线程里也有这么一把锁——互斥锁mutex互斥锁是一种简单的加锁的方法来控制对共享资源的访问互斥锁只有两种状态,即上锁( lock )和解锁( unlock )。2.1 互斥锁的特点1. 原子性把一个互斥量锁定为一个原子操作这意味着如果一个线程锁定了一个互斥量没有其他线程在同一时间可以成功锁定这个互斥量2. 唯一性如果一个线程锁定了一个互斥量在它解除锁定之前没有其他线程可以锁定这个互斥量3. 非繁忙等待如果一个线程已经锁定了一个互斥量第二个线程又试图去锁定这个互斥量则第二个线程将被挂起不占用任何cpu资源直到第一个线程解除对这个互斥量的锁定为止第二个线程则被唤醒并继续执行同时锁定这个互斥量。2.2 互斥锁的使用根据前面我们可以知道互斥锁主要就是用来保护共享资源的在C 11中互斥锁封装在mutex类中通过调用类成员函数lock()和unlock()来实现加锁和解锁。值得注意的是加锁和解锁必须成对使用这也是比较好理解的。除此之外互斥量的使用时机就以开篇程序为例我们要保护的共享资源当然就是消息队列list了那么互斥锁应该加在哪里呢可能想的比较简单一点就直接把锁加在函数最前面不就好了么如下所示class msgList { private: listintmylist; //用list模仿一个消息队列 mutex mtx; //创建互斥锁对象 public: void WriteList() //向消息队列中写入消息(以i作为消息) { mtx.lock(); for (int i 0; i100000; i) { cout Write : i endl; mylist.push_back(i); } mtx.unlock(); return; } //....... };不过如果这样加锁的话要等写线程完全执行结束才能开始读线程读写线程变成了串行执行这就违背了线程并发性的特点了。正确的加锁方式应当是在执行写操作的具体部分加锁如下所示class msgList { private: listintmylist; //用list模仿一个消息队列 mutex mtx; //创建互斥锁对象 public: void WriteList() //向消息队列中写入消息(以i作为消息) { for (int i 0; i100000; i) { mtx.lock(); cout Write : i endl; mylist.push_back(i); mtx.unlock(); } return; } //....... };这样才能真正的实现读写互不干扰。下面再举一个更为直观的例子创建两个线程同时对list进行写操作#include iostream #include list #include mutex #include thread using namespace std; class msgList { private: listint mylist; mutex m; int i 0; public: void WriteList() { while (true) { int val; { // 临界区读判断 自增 插入三步必须在一个锁内 lock_guardmutex lk(m); if (i 1000) break; val i; mylist.push_back(val); } // 出作用域自动解锁 // 锁外可做其他无需互斥的事 } } void showList() { lock_guardmutex lk(m); // 若可能与其他线程并发读也要加锁 for (auto p mylist.begin(); p ! mylist.end(); p) cout (*p) ; cout endl; cout size of list : mylist.size() endl; } }; int main() { msgList mlist; thread pwrite0(msgList::WriteList, mlist); thread pwrite1(msgList::WriteList, mlist); pwrite0.join(); pwrite1.join(); cout threads end! endl; mlist.showList(); //子线程结束后主线程打印list return 0; }link
返回列表