多線程之線程同步

線程同步,如果光從字面上看,這四個字並不好理解。什麼叫線程的同步?讓線程同時都在運行,顯然不是如此。多線程的出現,就是爲了讓每個子線程做獨立的事情,而這裏面經常發生的一個問題是,子線程做獨立的事情時卻要使用同一個資源(即共享資源,常常是全局變量)。所以,我更喜歡稱之爲線程的協調,使線程協調訪問共享資源,而不是在同一時刻訪問它。

舉個例子,我們平時的火車售票系統,其中定義了一個變量tickets,是全局變量,也是各個子線程都可以訪問的共享資源。這個時候,如果有兩個子線程同時訪問這個變量,勢必會出現售票混亂的問題。當然,文字性的敘述永遠都不夠清晰明瞭。

實現線程的協調,有三種方式(我現在只知道3種),分別是通過創建互斥對象函數(CreateMutex)、創建事件對象函數(CreateEvent)、初始化臨界區對象函數(InitializeCriticalSection)來實現。

 

代碼1(創建互斥對象函數):

  1. #include <windows.h>  
  2. #include <iostream.h>  
  3.  
  4. int tickets=100; //共享資源  
  5. HANDLE hMutex;   //共享對象(因爲是全局變量嘛~)  
  6.  
  7. //線程入口函數的聲明  
  8. DWORD WINAPI Func1Proc(LPVOID lpParameter);  
  9. DWORD WINAPI Func2Proc(LPVOID lpParameter);  
  10.  
  11. //主線程  
  12. int main()  
  13. {  
  14.     HANDLE hThread1;  
  15.     HANDLE hThread2;  
  16.  
  17.     /*安全屬性(SecurityAttributes),  
  18.      *是否擁有互斥對象(InitialOwner),  
  19.      *是否是匿名的互斥對象(MutexName)  
  20.      */ 
  21.     hMutex=CreateMutex(NULL,FALSE,NULL);   
  22.       
  23.     hThread1=CreateThread(NULL,0,Func1Proc,NULL,0,NULL);  
  24.     hThread2=CreateThread(NULL,0,Func2Proc,NULL,0,NULL);  
  25.     CloseHandle(hThread1);  
  26.     CloseHandle(hThread2);  
  27.       
  28.     Sleep(4000);  
  29.       
  30.     return 0;  
  31. }  
  32.  
  33. //線程1的入口函數  
  34. DWORD WINAPI Func1Proc(LPVOID lpParameter)  
  35. {  
  36.     while(true)  
  37.     {  
  38.         WaitForSingleObject(hMutex,INFINITE);  
  39.         if(tickets>0)  
  40.         {  
  41.            
  42.            cout<<"thread1 sell tickets: "<<tickets--<<endl;  //tickets先輸出,再減1  
  43.         }  
  44.         else 
  45.         {  
  46.            break;  
  47.         }  
  48.         ReleaseMutex(hMutex);  
  49.     }  
  50.       
  51.     return 0;  
  52. }  
  53. //線程2的入口函數  
  54. DWORD WINAPI Func2Proc(LPVOID lpParameter)  
  55. {  
  56.     while(true)  
  57.     {  
  58.         WaitForSingleObject(hMutex,INFINITE);  
  59.         if(tickets>0)  
  60.         {  
  61.              
  62.            cout<<"thread2 sell tickets: "<<tickets--<<endl;  //tickets先輸出,再減1  
  63.         }  
  64.         else 
  65.         {  
  66.            break;  
  67.         }  
  68.         ReleaseMutex(hMutex);  
  69.     }  
  70.       
  71.     return 0;  
  72. }  

需要注意的是,CreateMutex函數的3個參數,說明如下:

HANDLE CreateMutex( 
  LPSECURITY_ATTRIBUTES lpMutexAttributes, 
  BOOL bInitialOwner, 
  LPCTSTR lpName 
);

第一個參數,表示默認的安全屬性;

第二個參數,表示創建互斥對象時主線程是否擁有互斥對象;

第三個參數,互斥對象名

 

代碼2(創建事件對象函數):

  1. #include <windows.h>  
  2. #include <iostream.h>  
  3.  
  4. int tickets=100; //共享資源  
  5. HANDLE hEvent;   //共享對象(因爲是全局變量嘛~)  
  6.  
  7. //線程入口函數的聲明  
  8. DWORD WINAPI Func1Proc(LPVOID lpParameter);  
  9. DWORD WINAPI Func2Proc(LPVOID lpParameter);  
  10.  
  11. //主線程  
  12. int main()  
  13. {  
  14.     HANDLE hThread1;  
  15.     HANDLE hThread2;  
  16.  
  17.     /*第三個參數FALSE,表示事件對象初始化爲無信號狀態  
  18.      *第二個參數FASLE,表示線程申請事件對象之後(自動設置爲無信號狀態)  
  19.      */ 
  20.     hEvent=CreateEvent(NULL,FALSE,TRUE,NULL);   
  21.       
  22.     hThread1=CreateThread(NULL,0,Func1Proc,NULL,0,NULL);  
  23.     hThread2=CreateThread(NULL,0,Func2Proc,NULL,0,NULL);  
  24.     CloseHandle(hThread1);  
  25.     CloseHandle(hThread2);  
  26.       
  27.     Sleep(4000);  
  28.       
  29.     return 0;  
  30. }  
  31.  
  32. //線程1的入口函數  
  33. DWORD WINAPI Func1Proc(LPVOID lpParameter)  
  34. {  
  35.     while(true)  
  36.     {  
  37.         WaitForSingleObject(hEvent,INFINITE);  
  38.         if(tickets>0)  
  39.         {  
  40.            
  41.            cout<<"thread1 sell tickets: "<<tickets--<<endl;  //tickets先輸出,再減1  
  42.            SetEvent(hEvent);  
  43.         }  
  44.         else 
  45.         {  
  46.            break;  
  47.         }  
  48.           
  49.     }  
  50.       
  51.     return 0;  
  52. }  
  53. //線程2的入口函數  
  54. DWORD WINAPI Func2Proc(LPVOID lpParameter)  
  55. {  
  56.     while(true)  
  57.     {  
  58.         WaitForSingleObject(hEvent,INFINITE);  
  59.         if(tickets>0)  
  60.         {  
  61.              
  62.            cout<<"thread2 sell tickets: "<<tickets--<<endl;  //tickets先輸出,再減1  
  63.            SetEvent(hEvent);  
  64.         }  
  65.         else 
  66.         {  
  67.            break;  
  68.         }  
  69.           
  70.     }  
  71.       
  72.     return 0;  
  73. }  

需要注意的是,CreateMutex函數的4個參數,說明如下:

HANDLE CreateEvent(
  LPSECURITY_ATTRIBUTES lpEventAttributes, 
  BOOL bManualReset, 
  BOOL bInitialState, 
  LPTSTR lpName 
); 

第一個參數,同上;

第二個參數,表示申請到線程互斥對象後是否手動設置爲無信號狀態;

第三個參數,表示事件對象的初始化狀態;(更正:代碼中的註釋行,應該是初始化爲有信號狀態

第四個參數,同上

 

代碼3(初始化臨界區對象函數):

  1. #include <windows.h>  
  2. #include <iostream.h>  
  3.  
  4. int tickets=100; //共享資源  
  5. CRITICAL_SECTION g_cs;   //共享對象(因爲是全局變量嘛~)  
  6.  
  7. //線程入口函數的聲明  
  8. DWORD WINAPI Func1Proc(LPVOID lpParameter);  
  9. DWORD WINAPI Func2Proc(LPVOID lpParameter);  
  10.  
  11. //主線程  
  12. int main()  
  13. {  
  14.     HANDLE hThread1;  
  15.     HANDLE hThread2;  
  16.  
  17.     /*似乎,臨界區對象最方便,至少初始化的時候就只需要一個參數  
  18.      *  
  19.      */ 
  20.     InitializeCriticalSection(&g_cs);   
  21.       
  22.     hThread1=CreateThread(NULL,0,Func1Proc,NULL,0,NULL);  
  23.     hThread2=CreateThread(NULL,0,Func2Proc,NULL,0,NULL);  
  24.     CloseHandle(hThread1);  
  25.     CloseHandle(hThread2);  
  26.       
  27.     Sleep(4000);  
  28.     DeleteCriticalSection(&g_cs);  
  29.       
  30.     return 0;  
  31. }  
  32.  
  33. //線程1的入口函數  
  34. DWORD WINAPI Func1Proc(LPVOID lpParameter)  
  35. {  
  36.     while(true)  
  37.     {  
  38.         EnterCriticalSection(&g_cs);  
  39.  
  40.         if(tickets>0)  
  41.         {  
  42.            
  43.            cout<<"thread1 sell tickets: "<<tickets--<<endl;  //tickets先輸出,再減1  
  44.         }  
  45.         else 
  46.         {  
  47.            break;  
  48.         }  
  49.  
  50.         LeaveCriticalSection(&g_cs);  
  51.           
  52.     }  
  53.       
  54.     return 0;  
  55. }  
  56. //線程2的入口函數  
  57. DWORD WINAPI Func2Proc(LPVOID lpParameter)  
  58. {  
  59.     while(true)  
  60.     {  
  61.         EnterCriticalSection(&g_cs);  
  62.  
  63.         if(tickets>0)  
  64.         {  
  65.              
  66.            cout<<"thread2 sell tickets: "<<tickets--<<endl;  //tickets先輸出,再減1  
  67.         }  
  68.         else 
  69.         {  
  70.            break;  
  71.         }  
  72.  
  73.         LeaveCriticalSection(&g_cs);  
  74.           
  75.     }  
  76.       
  77.     return 0;  
  78. }  

 

 

我現在想說的是,如果綜合起來看的話,對於第一種方法,需要通過WaitForSingleObject函數來申請互斥對象,一旦申請到後,(操作系統會將互斥對象設置爲無信號狀態),以防止其它線程申請。當售完票之後,該線程必須調用ReleaseMutex函數釋放互斥對象,(操作系統又會將互斥對象設置爲有信號狀態)。這時,其它線程就可以申請互斥對象。如此循環,即可實現線程的協調。

對於第三種方法,原理一樣,無非換成了EnterCriticalSection函數和LeaveCriticalSection函數。

而第二種方法,大致也是如此,只是換成了WaitForSingleObject函數和SetEvent函數。

但這幾種線程協調的方法,肯定有不同之處(不然弄三個這玩意幹嘛~),至於具體差別在哪兒,我也不是很清楚。因爲書上也沒有寫清楚。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章