Mutex是什么
Mutex,叫互斥锁。
在编程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。每个对象都对应于一个可称为” 互斥锁” 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
为什么使用Mutex
因为线程存在并发性,并发容易导致问题。举个例子:A,B两个售票员,看到最后还有一张车票。同时,正好有C,D两个人要买这张车票。那么这个车票怎么卖?A认为可以卖给C,B认为可以卖给D。但是最后如果C,D同时买到这张车票,那么谁来坐这个车票的座位呢?
看看程序中的例子。假设有1000张车票,分别由ticketSellThread1和ticketSellThread2来进行销售。
#include <OpenThreads/Thread>
#include <iostream>
int g_ticketCounts = 1000;
class TicketSellThread : public OpenThreads::Thread
{
public:
virtual void run()
{
for (int i = 0; i < 500; i++)
{
g_ticketCounts--;
std::cout << g_ticketCounts << std::endl;
}
}
};
int main(int argc, char** argv)
{
TicketSellThread ticketSellThread1;
ticketSellThread1.start();
TicketSellThread ticketSellThread2;
ticketSellThread2.start();
//等待ticketSellThread1运行完
while(ticketSellThread1.isRunning())
OpenThreads::Thread::YieldCurrentThread();
//等待ticketSellThread1运行完
while(ticketSellThread2.isRunning())
OpenThreads::Thread::YieldCurrentThread();
return 0;
}
每个线程分别销售500张。那么到最后,这个车票数量应该为0。但是实际情况呢?
实际最后还剩下12张。你们的电脑上可能情况不一样,但是最终结果,为一个随机值,很大可能不为0。这是怎么回事?ticketSellThread1和ticketSellThread2都卖了500张,但是最后还剩12张。这中间肯定出现了一张票,卖给2个人的情况。这里,有12张票卖重复了。
那怎么避免这种情况呢?我要保证,一张票,在同一个时间点,只有一个售票员卖。可以这样,A售票员在卖出88号票之前,先给88号票上一把锁。然后B售票员,看到这个88号票,上锁了,就不卖88号票了。
程序中,上锁的方式,有好几种。使用Mutex是其中一种方式。
如何使用Mutex
Mutex的使用,也比较简单。定义一个Mutex,然后使用lock函数,进行上锁,使用unlock函数,进行解锁。下面程序,在g_ticketCounts–之前,对g_ticketCounts进行上锁,然后减完之后,进行解锁。最后运行结果,g_ticketCounts为0,表示所有票正好卖完,没有重复的了。
#include <OpenThreads/Thread>
#include <OpenThreads/Mutex>
#include <iostream>
int g_ticketCounts = 1000;
OpenThreads::Mutex g_ticketMutex;
class TicketSellThread : public OpenThreads::Thread
{
public:
virtual void run()
{
for (int i = 0; i < 500; i++)
{
g_ticketMutex.lock();
g_ticketCounts--;
g_ticketMutex.unlock();
std::cout << g_ticketCounts << std::endl;
}
}
};
int main(int argc, char** argv)
{
TicketSellThread ticketSellThread1;
ticketSellThread1.start();
TicketSellThread ticketSellThread2;
ticketSellThread2.start();
//等待ticketSellThread1运行完
while(ticketSellThread1.isRunning())
OpenThreads::Thread::YieldCurrentThread();
//等待ticketSellThread1运行完
while(ticketSellThread2.isRunning())
OpenThreads::Thread::YieldCurrentThread();
return 0;
}
但是Mutex的使用,一定要记得解锁,否则会引起各种问题。
比如下面的代码,当i==5时,我在lock和unlock之间,抛出一个异常,导致线程终止,mutex一直没有执行unlock,一直处于锁住状态。程序没法继续运行。
virtual void run()
{
for (int i = 0; i < 500; i++)
{
g_ticketMutex.lock();
g_ticketCounts--;
if (i == 5)
{
throw std::exception();
}
g_ticketMutex.unlock();
std::cout << g_ticketCounts << std::endl;
}
}
这种情况怎么办呢?OpenThreads提供了一个叫做范围锁的东西,ScopedLock。这个类的功能,其实很简单,在它的构造函数中,调用mutex的lock函数,在析构函数中,调用mutex的unlock函数。这样就可以确保mutex的unlock被执行,无论发生了什么。
virtual void run()
{
for (int i = 0; i < 500; i++)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> mutloc(g_ticketMutex);
g_ticketCounts--;
if (i == 5)
{
throw std::exception();
}
std::cout << g_ticketCounts << std::endl;
}
}
对应的头文件包含为#include <OpenThreads/ScopedLock>
。