線程同步,如果光從字面上看,這四個字並不好理解。什麼叫線程的同步?讓線程同時都在運行,顯然不是如此。多線程的出現,就是爲了讓每個子線程做獨立的事情,而這裏面經常發生的一個問題是,子線程做獨立的事情時卻要使用同一個資源(即共享資源,常常是全局變量)。所以,我更喜歡稱之爲線程的協調,使線程協調訪問共享資源,而不是在同一時刻訪問它。
舉個例子,我們平時的火車售票系統,其中定義了一個變量tickets,是全局變量,也是各個子線程都可以訪問的共享資源。這個時候,如果有兩個子線程同時訪問這個變量,勢必會出現售票混亂的問題。當然,文字性的敘述永遠都不夠清晰明瞭。
實現線程的協調,有三種方式(我現在只知道3種),分別是通過創建互斥對象函數(CreateMutex)、創建事件對象函數(CreateEvent)、初始化臨界區對象函數(InitializeCriticalSection)來實現。
代碼1(創建互斥對象函數):
- #include <windows.h>
- #include <iostream.h>
- int tickets=100; //共享資源
- HANDLE hMutex; //共享對象(因爲是全局變量嘛~)
- //線程入口函數的聲明
- DWORD WINAPI Func1Proc(LPVOID lpParameter);
- DWORD WINAPI Func2Proc(LPVOID lpParameter);
- //主線程
- int main()
- {
- HANDLE hThread1;
- HANDLE hThread2;
- /*安全屬性(SecurityAttributes),
- *是否擁有互斥對象(InitialOwner),
- *是否是匿名的互斥對象(MutexName)
- */
- hMutex=CreateMutex(NULL,FALSE,NULL);
- hThread1=CreateThread(NULL,0,Func1Proc,NULL,0,NULL);
- hThread2=CreateThread(NULL,0,Func2Proc,NULL,0,NULL);
- CloseHandle(hThread1);
- CloseHandle(hThread2);
- Sleep(4000);
- return 0;
- }
- //線程1的入口函數
- DWORD WINAPI Func1Proc(LPVOID lpParameter)
- {
- while(true)
- {
- WaitForSingleObject(hMutex,INFINITE);
- if(tickets>0)
- {
- cout<<"thread1 sell tickets: "<<tickets--<<endl; //tickets先輸出,再減1
- }
- else
- {
- break;
- }
- ReleaseMutex(hMutex);
- }
- return 0;
- }
- //線程2的入口函數
- DWORD WINAPI Func2Proc(LPVOID lpParameter)
- {
- while(true)
- {
- WaitForSingleObject(hMutex,INFINITE);
- if(tickets>0)
- {
- cout<<"thread2 sell tickets: "<<tickets--<<endl; //tickets先輸出,再減1
- }
- else
- {
- break;
- }
- ReleaseMutex(hMutex);
- }
- return 0;
- }
需要注意的是,CreateMutex函數的3個參數,說明如下:
HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCTSTR lpName );
第一個參數,表示默認的安全屬性;
第二個參數,表示創建互斥對象時主線程是否擁有互斥對象;
第三個參數,互斥對象名
代碼2(創建事件對象函數):
- #include <windows.h>
- #include <iostream.h>
- int tickets=100; //共享資源
- HANDLE hEvent; //共享對象(因爲是全局變量嘛~)
- //線程入口函數的聲明
- DWORD WINAPI Func1Proc(LPVOID lpParameter);
- DWORD WINAPI Func2Proc(LPVOID lpParameter);
- //主線程
- int main()
- {
- HANDLE hThread1;
- HANDLE hThread2;
- /*第三個參數FALSE,表示事件對象初始化爲無信號狀態
- *第二個參數FASLE,表示線程申請事件對象之後(自動設置爲無信號狀態)
- */
- hEvent=CreateEvent(NULL,FALSE,TRUE,NULL);
- hThread1=CreateThread(NULL,0,Func1Proc,NULL,0,NULL);
- hThread2=CreateThread(NULL,0,Func2Proc,NULL,0,NULL);
- CloseHandle(hThread1);
- CloseHandle(hThread2);
- Sleep(4000);
- return 0;
- }
- //線程1的入口函數
- DWORD WINAPI Func1Proc(LPVOID lpParameter)
- {
- while(true)
- {
- WaitForSingleObject(hEvent,INFINITE);
- if(tickets>0)
- {
- cout<<"thread1 sell tickets: "<<tickets--<<endl; //tickets先輸出,再減1
- SetEvent(hEvent);
- }
- else
- {
- break;
- }
- }
- return 0;
- }
- //線程2的入口函數
- DWORD WINAPI Func2Proc(LPVOID lpParameter)
- {
- while(true)
- {
- WaitForSingleObject(hEvent,INFINITE);
- if(tickets>0)
- {
- cout<<"thread2 sell tickets: "<<tickets--<<endl; //tickets先輸出,再減1
- SetEvent(hEvent);
- }
- else
- {
- break;
- }
- }
- return 0;
- }
需要注意的是,CreateMutex函數的4個參數,說明如下:
HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPTSTR lpName );
第一個參數,同上;
第二個參數,表示申請到線程互斥對象後是否手動設置爲無信號狀態;
第三個參數,表示事件對象的初始化狀態;(更正:代碼中的註釋行,應該是初始化爲有信號狀態)
第四個參數,同上
代碼3(初始化臨界區對象函數):
- #include <windows.h>
- #include <iostream.h>
- int tickets=100; //共享資源
- CRITICAL_SECTION g_cs; //共享對象(因爲是全局變量嘛~)
- //線程入口函數的聲明
- DWORD WINAPI Func1Proc(LPVOID lpParameter);
- DWORD WINAPI Func2Proc(LPVOID lpParameter);
- //主線程
- int main()
- {
- HANDLE hThread1;
- HANDLE hThread2;
- /*似乎,臨界區對象最方便,至少初始化的時候就只需要一個參數
- *
- */
- InitializeCriticalSection(&g_cs);
- hThread1=CreateThread(NULL,0,Func1Proc,NULL,0,NULL);
- hThread2=CreateThread(NULL,0,Func2Proc,NULL,0,NULL);
- CloseHandle(hThread1);
- CloseHandle(hThread2);
- Sleep(4000);
- DeleteCriticalSection(&g_cs);
- return 0;
- }
- //線程1的入口函數
- DWORD WINAPI Func1Proc(LPVOID lpParameter)
- {
- while(true)
- {
- EnterCriticalSection(&g_cs);
- if(tickets>0)
- {
- cout<<"thread1 sell tickets: "<<tickets--<<endl; //tickets先輸出,再減1
- }
- else
- {
- break;
- }
- LeaveCriticalSection(&g_cs);
- }
- return 0;
- }
- //線程2的入口函數
- DWORD WINAPI Func2Proc(LPVOID lpParameter)
- {
- while(true)
- {
- EnterCriticalSection(&g_cs);
- if(tickets>0)
- {
- cout<<"thread2 sell tickets: "<<tickets--<<endl; //tickets先輸出,再減1
- }
- else
- {
- break;
- }
- LeaveCriticalSection(&g_cs);
- }
- return 0;
- }
我現在想說的是,如果綜合起來看的話,對於第一種方法,需要通過WaitForSingleObject函數來申請互斥對象,一旦申請到後,(操作系統會將互斥對象設置爲無信號狀態),以防止其它線程申請。當售完票之後,該線程必須調用ReleaseMutex函數釋放互斥對象,(操作系統又會將互斥對象設置爲有信號狀態)。這時,其它線程就可以申請互斥對象。如此循環,即可實現線程的協調。
對於第三種方法,原理一樣,無非換成了EnterCriticalSection函數和LeaveCriticalSection函數。
而第二種方法,大致也是如此,只是換成了WaitForSingleObject函數和SetEvent函數。
但這幾種線程協調的方法,肯定有不同之處(不然弄三個這玩意幹嘛~),至於具體差別在哪兒,我也不是很清楚。因爲書上也沒有寫清楚。