上篇提到线程针对临界值操作时需要加锁,但是线程访问临界资源只通过锁来控制是不够的。

比如对一个数据进行操作,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。再进行正确操作。