在使用线程时,经常要注意的就是访问临界资源加锁。
在编码过程由于粗心忘记加锁将带来不可预知的错误。这类错误单次运行或小并发时难以复现,当数据量变大,用户数增多时,轻则系统崩溃,大则引起数据错误。造成损失。
线程中互斥锁与进程的信号量类似,也可以看做是PV操作,用于保护临界资源,确保只有一个线程访问。
下面代码是不加锁错误代码,其中也涉及到之前提到的线程编程时需要注意的一些小细节。
1 #include <pthread.h> 2 #include <unistd.h> 3 #include <iostream> 4 5 using namespace std; 6 7 class ThreadInterface 8 { 9 public: 10 void CreateThread(void* (*func)(void *)); 11 void WaitThread(); 12 private: 13 pthread_t m_pTread; 14 }; 15 16 void ThreadInterface::CreateThread(void* (*func)(void *)) 17 { 18 pthread_create(&m_pTread, NULL, func, NULL); 19 } 20 21 void ThreadInterface::WaitThread() 22 { 23 pthread_join(m_pTread, NULL); 24 } 25 26 class MutexLockInterface 27 { 28 public: 29 void CreateMutexLock(); 30 void GetMutexLock(); 31 void ReleaseMutexLock(); 32 void DestroyMutexLock(); 33 private: 34 pthread_mutex_t m_MutexLock; 35 }; 36 37 void MutexLockInterface::CreateMutexLock() 38 { 39 int ret = pthread_mutex_init(&m_MutexLock, NULL); 40 if (0 != ret) 41 cout<<"init mutex error!"; 42 } 43 44 void MutexLockInterface::GetMutexLock() 45 { 46 pthread_mutex_lock(&m_MutexLock); 47 } 48 49 void MutexLockInterface::ReleaseMutexLock() 50 { 51 pthread_mutex_unlock(&m_MutexLock); 52 } 53 54 void MutexLockInterface::DestroyMutexLock() 55 { 56 pthread_mutex_destroy(&m_MutexLock); 57 } 58 59 60 class Service 61 { 62 public: 63 static void* run(void *) //类成员线程函数为static去除this指针 64 { 65 //m_MutexLock.GetMutexLock(); 66 if (0 == m_Tickets) 67 { 68 cout<<"stop operate!"<<endl; 69 } 70 else 71 { 72 cout<<"window2:we have "<<m_Tickets<<"Tickets"<<endl; 73 sleep(1); 74 --m_Tickets; 75 } 76 //m_MutexLock.ReleaseMutexLock(); 77 } 78 int SetData(int data){m_Tickets = data;}; 79 int GetData(){return m_Tickets;}; 80 static int m_Tickets; 81 static MutexLockInterface m_MutexLock; 82 }; 83 int Service::m_Tickets = 1; //静态变量类外初始化 84 MutexLockInterface Service::m_MutexLock; 85 86 int main() 87 { 88 Service Srv; 89 ThreadInterface Thread; 90 Srv.m_MutexLock.CreateMutexLock(); 91 92 Thread.CreateThread(&Srv.run); 93 94 //Srv.m_MutexLock.GetMutexLock(); 95 if (0 == Srv.GetData()) 96 { 97 cout<<"stop operate!"<<endl; 98 } 99 else 100 { 101 cout<<"window1:we have "<<Srv.GetData()<<"Tickets"<<endl; 102 sleep(1); //延时1s等待线程2 103 Srv.SetData(Srv.GetData() - 1); 104 } 105 //Srv.m_MutexLock.ReleaseMutexLock(); 106 Thread.WaitThread(); //等待线程结束回收 107 cout<<Srv.GetData()<<endl; 108 return 0; 109 }
上述代码以售票为场景,当票只剩下一张时,两个窗口同时有人需要购票。
线程不加锁,执行结果如下:
很显然这不是我们想要的结果,只有一张票却卖出去了两张,最后余票显示为-1!
去除注释行,对临界资源操作是加锁,再运行程序,得到与预期一致的结果!
这就是线程互斥锁存在的原因。