在多任务操作系统中,同时运行的多个任务可能都需要使用同一种资源。比如说,同一个文件,可能一个线程会对其进行写操作,而另一个线程需要对这个文件进行读操作,可想而知,如果写线程还没有写结束,而此时读线程开始了,或者读线程还没有读结束而写线程开始了,那么最终的结果显然会是混乱的。为了保护共享资源,在线程里也有这么一把锁——互斥锁(mutex),互斥锁是一种简单的加锁的方法来控制对共享资源的访问,互斥锁只有两种状态,即上锁( lock )和解锁( unlock )。
互斥锁的特点
1. 原子性:把一个互斥量锁定为一个原子操作,这意味着如果一个线程锁定了一个互斥量,没有其他线程在同一时间可以成功锁定这个互斥量;
2. 唯一性:如果一个线程锁定了一个互斥量,在它解除锁定之前,没有其他线程可以锁定这个互斥量;
3. 非繁忙等待:如果一个线程已经锁定了一个互斥量,第二个线程又试图去锁定这个互斥量,则第二个线程将被挂起(不占用任何cpu资源),直到第一个线程解除对这个互斥量的锁定为止,第二个线程则被唤醒并继续执行,同时锁定这个互斥量。
互斥锁的使用
根据前面我们可以知道,互斥锁主要就是用来保护共享资源的,在C++ 11中,互斥锁封装在mutex类中,通过调用类成员函数lock()和unlock()来实现加锁和解锁。值得注意的是,加锁和解锁,必须成对使用,这也是比较好理解的。除此之外,互斥量的使用时机,就以开篇程序为例,我们要保护的共享资源当然就是消息队列list了,那么互斥锁应该加在哪里呢?
#include<iostream>
#include<list>
#include"pthread.h"
#include"thread"
#include<mutex>
using namespace std;
class msgList
{
private:
list<int>mylist; //用list模仿一个消息队列
mutex mtx;
public:
void WriteList() //向消息队列中写入消息(以i作为消息)
{
for (int i = 0; i < 1000; i++)
{
mtx.lock();
cout << "Write : " << i << endl;
mylist.push_back(i);
mtx.unlock();
}
return;
}
void ReadList() //从消息队列中读取并取出消息
{
for (int i = 0; i < 1000; i++)
{
if (!mylist.empty())
{
mtx.lock();
cout << "Read : " << mylist.front() << endl;
mylist.pop_front();
mtx.unlock();
}
else
{
mtx.lock();
cout << "Message List is empty!" << endl;
mtx.unlock();
}
}
}
};