在使用线程时,经常要注意的就是访问临界资源加锁。 在编码过程由于粗心忘记加锁将带来不可预知的错误。这类错误单次运行或小并发时难以复现,当数据量变大,用户数增多时,轻则系统崩溃,大则引起数据错误。造成损失。 线程中互斥锁与进程的信号量类似,也可以看做是PV操作,用于保护临界资源,确保只有一个线程访问。
下面代码是不加锁错误代码,其中也涉及到之前提到的线程编程时需要注意的一些小细节。
代码语言:javascript复制#include <pthread.h>
#include <unistd.h>
#include <iostream>
using namespace std;
class ThreadInterface
{
public:
void CreateThread(void* (*func)(void *));
void WaitThread();
private:
pthread_t m_pTread;
};
void ThreadInterface::CreateThread(void* (*func)(void *))
{
pthread_create(&m_pTread, NULL, func, NULL);
}
void ThreadInterface::WaitThread()
{
pthread_join(m_pTread, NULL);
}
class MutexLockInterface
{
public:
void CreateMutexLock();
void GetMutexLock();
void ReleaseMutexLock();
void DestroyMutexLock();
private:
pthread_mutex_t m_MutexLock;
};
void MutexLockInterface::CreateMutexLock()
{
int ret = pthread_mutex_init(&m_MutexLock, NULL);
if (0 != ret)
cout<<"init mutex error!";
}
void MutexLockInterface::GetMutexLock()
{
pthread_mutex_lock(&m_MutexLock);
}
void MutexLockInterface::ReleaseMutexLock()
{
pthread_mutex_unlock(&m_MutexLock);
}
void MutexLockInterface::DestroyMutexLock()
{
pthread_mutex_destroy(&m_MutexLock);
}
class Service
{
public:
static void* run(void *) //类成员线程函数为static去除this指针
{
//m_MutexLock.GetMutexLock();
if (0 == m_Tickets)
{
cout<<"stop operate!"<<endl;
}
else
{
cout<<"window2:we have "<<m_Tickets<<"Tickets"<<endl;
sleep(1);
--m_Tickets;
}
//m_MutexLock.ReleaseMutexLock();
}
int SetData(int data){m_Tickets = data;};
int GetData(){return m_Tickets;};
static int m_Tickets;
static MutexLockInterface m_MutexLock;
};
int Service::m_Tickets = 1; //静态变量类外初始化
MutexLockInterface Service::m_MutexLock;
int main()
{
Service Srv;
ThreadInterface Thread;
Srv.m_MutexLock.CreateMutexLock();
Thread.CreateThread(&Srv.run);
//Srv.m_MutexLock.GetMutexLock();
if (0 == Srv.GetData())
{
cout<<"stop operate!"<<endl;
}
else
{
cout<<"window1:we have "<<Srv.GetData()<<"Tickets"<<endl;
sleep(1); //延时1s等待线程2
Srv.SetData(Srv.GetData() - 1);
}
//Srv.m_MutexLock.ReleaseMutexLock();
Thread.WaitThread(); //等待线程结束回收
cout<<Srv.GetData()<<endl;
return 0;
}
上述代码以售票为场景,当票只剩下一张时,两个窗口同时有人需要购票。
线程不加锁,执行结果如下:
很显然这不是我们想要的结果,只有一张票却卖出去了两张,最后余票显示为-1!
去除注释行,对临界资源操作是加锁,再运行程序,得到与预期一致的结果!
这就是线程互斥锁存在的原因。