上篇提到线程针对临界值操作时需要加锁,但是线程访问临界资源只通过锁来控制是不够的。
比如对一个数据进行操作,A线程需要读,B线程进行写。
A线程先访问临界资源,发现没有数据可以读,只能等待B线程先写,此时又占用了互斥锁,导致B线程无法得到锁,进行写操作。
此时就需要用到条件变量了,条件变量的目的就是控制线程的先后执行,保证临界资源的有效性。
下面依然是售票的一个场景,此时一个线程售票,一个线程退票。
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 pthread_mutex_t m_MutexLock; 34 }; 35 36 void MutexLockInterface::CreateMutexLock() 37 { 38 int ret = pthread_mutex_init(&m_MutexLock, NULL); 39 if (0 != ret) 40 cout<<"init mutex error!"; 41 } 42 43 void MutexLockInterface::GetMutexLock() 44 { 45 pthread_mutex_lock(&m_MutexLock); 46 } 47 48 void MutexLockInterface::ReleaseMutexLock() 49 { 50 pthread_mutex_unlock(&m_MutexLock); 51 } 52 53 void MutexLockInterface::DestroyMutexLock() 54 { 55 pthread_mutex_destroy(&m_MutexLock); 56 } 57 58 class CondInterface 59 { 60 public: 61 void CreateCond(); 62 void WaitCond(pthread_mutex_t *mutex); 63 void WakeupCond(); 64 void DestroyCond(); 65 private: 66 pthread_cond_t m_Cond; 67 }; 68 69 void CondInterface::CreateCond() 70 { 71 int ret = pthread_cond_init(&m_Cond, NULL); 72 if (0 != ret) 73 cout<<"init mutex error!"; 74 } 75 void CondInterface::WaitCond(pthread_mutex_t *mutex) 76 { 77 pthread_cond_wait(&m_Cond, mutex); 78 } 79 void CondInterface::WakeupCond() 80 { 81 pthread_cond_broadcast(&m_Cond); 82 } 83 void CondInterface::DestroyCond() 84 { 85 pthread_cond_destroy(&m_Cond); 86 } 87 88 class Service 89 { 90 public: 91 static void* run(void *) 92 { 93 m_MutexLock.GetMutexLock(); 94 cout<<"we have "<<m_Tickets<<"Tickets"<<endl; 95 sleep(1); 96 m_Tickets++; 97 //Cond.WakeupCond(); 98 m_MutexLock.ReleaseMutexLock(); 99 } 100 int SetData(int data){m_Tickets = data;}; 101 int GetData(){return m_Tickets;}; 102 static int m_Tickets; 103 static MutexLockInterface m_MutexLock; 104 static CondInterface Cond; 105 }; 106 int Service::m_Tickets = 0; 107 MutexLockInterface Service::m_MutexLock; 108 CondInterface Service::Cond; 109 110 int main() 111 { 112 Service Srv; 113 ThreadInterface Thread; 114 Srv.m_MutexLock.CreateMutexLock(); 115 Srv.Cond.CreateCond(); 116 Thread.CreateThread(&Srv.run); 117 118 Srv.m_MutexLock.GetMutexLock(); 119 if (0 == Srv.GetData()) 120 { 121 //Srv.Cond.WaitCond(&Srv.m_MutexLock.m_MutexLock); 122 cout<<"wait!"<<endl; 123 } 124 125 cout<<"window1:we have "<<Srv.GetData()<<"Tickets"<<endl; 126 sleep(1); 127 Srv.SetData(Srv.GetData() - 1); 128 129 Srv.m_MutexLock.ReleaseMutexLock(); 130 Thread.WaitThread(); 131 cout<<Srv.GetData()<<endl; 132 return 0; 133 }
不使用条件变量执行结果如下:
线程1先执行,此时并没有票。此时应该先放弃锁,让线程2先执行,取消注释执行结果如下:
由此可以看出,条件变量让线程1暂时先放弃锁进入阻塞,等线程2执行完毕后,唤醒线程1。再进行正确操作。