用於線程互斥的方法有:原子鎖,關鍵地區(CriticalSection),互斥量(Mutex),。
用於線程同步的方法有:事件(Event),訊號量(Semaphore),定時器(這裡我們不談)。
原子鎖:我們都知道簡單的i++,並不是原子操作,在內部其實分了幾步來做,在這種情況下使用多線程就會出問題。所以我們可以採用原子鎖,但是這種方法有一定的局限性,那就是只能是執行加減的簡單資料的操作,如果涉及到複雜的資料結構,那麼就會出問題。
InterlockedIncrement(LONG volatile*Addend);
InterlockedDecrement(LONG volatile*Addend);
InterlockedExchangeAdd(LONG volatile*Addend,
LONGValue);
InterlockedExchange(LONG volatile*Target,
LONGValue);
例子:
沒有使用原子鎖的情況:
#include <stdio.h>#include <windows.h>#include <process.h>volatile long g_nLoginCount; unsigned int __stdcall Fun(void *pPM); const DWORD THREAD_NUM = 50;unsigned int __stdcall ThreadFun(void *pPM){g_nLoginCount++;return 0;}int main(){int num= 20;while (num--){g_nLoginCount = 0;int i;HANDLE handle[THREAD_NUM];for (i = 0; i < THREAD_NUM; i++)handle[i] = (HANDLE )_beginthreadex(NULL, 0, ThreadFun, NULL, 0, NULL);WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);for(i=0;i<THREAD_NUM;i++){CloseHandle(handle[i]);}printf("%d個線程執行i++,結果是%d\n", THREAD_NUM, g_nLoginCount);}return 0;}
這裡解釋下為什麼搞幾個線程,還弄的這麼複雜,一個while迴圈,一個for迴圈,這是因為不能一次建立很多線程,否則會出問題,你自己可以試試依次性建立100個線程試試,會出問題的。貌似一次最多隻能建立64個線程。所以這裡採用了這麼複雜的方法。
使用原子鎖的情況:
unsigned int __stdcall ThreadFun(void *pPM){g_nLoginCount++;InterlockedIncrement(&g_nLoginCount);return 0;}
關鍵地區(CriticalSection)一般線程互斥用的多的是這種情況。
void
InitializeCriticalSection(LPCRITICAL_SECTIONlpCriticalSection); -----------初始化關鍵地區
void
DeleteCriticalSection(LPCRITICAL_SECTIONlpCriticalSection); -----------刪除關鍵地區
void
EnterCriticalSection(LPCRITICAL_SECTIONlpCriticalSection); -----------進入關鍵地區void
LeaveCriticalSection(LPCRITICAL_SECTIONlpCriticalSection); -----------退出關鍵地區
一般錯誤的情況:
#include <stdio.h>#include <process.h>#include <windows.h>long g_nNum;unsigned int __stdcall Fun(void *pPM);const int THREAD_NUM = 10;int main(){HANDLE handle[THREAD_NUM];g_nNum = 0;int i = 0;while (i < THREAD_NUM) {handle[i++] = (HANDLE)_beginthreadex(NULL, 0, Fun, NULL, 0, NULL);}WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);for(i=0;i<sizeof(handle);i++){CloseHandle(handle[i]);}return 0;}unsigned int __stdcall Fun(void *pPM){Sleep(50);g_nNum++;Sleep(0);printf("當前計數為:%d\n",g_nNum);return 0;}
當使用了關鍵地區的情況:
CRITICAL_SECTION g_csThreadCode;InitializeCriticalSection(&g_csThreadCode);DeleteCriticalSection(&g_csThreadCode);
unsigned int __stdcall Fun(void *pPM){Sleep(50);EnterCriticalSection(&g_csThreadCode);g_nNum++;Sleep(0);printf("當前計數為:%d\n",g_nNum);LeaveCriticalSection(&g_csThreadCode);return 0;}
事件(Event)
HANDLECreateEvent(
LPSECURITY_ATTRIBUTESlpEventAttributes,
BOOLbManualReset,
BOOLbInitialState,
LPCTSTRlpName
);---------建立事件。
HANDLEOpenEvent(
DWORDdwDesiredAccess,
BOOLbInheritHandle,
LPCTSTRlpName
);------------開啟事件
BOOLSetEvent(HANDLEhEvent);----------觸發事件
BOOLResetEvent(HANDLEhEvent);-----------將事件設為未觸發
一般事件分成兩種:手動置位事件(TRUE)和自動置位事件(FALSE),這是在CreateEvent函數的第二個參數指定的。它們的區別是:手動置位事件當調用SetEvent後,所有等待這個事件的線程啟用,而自動置位事件調用SetEvent後,只有一個線程被啟用。自動置位事件不用調用ResetEvent函數,因為系統會自動幫你置為未觸發,而手動置位事件還要調用ResetEvent函數。
在展示事件之前,我們先來說說線程互斥和線程同步之間的區別吧。我的理解是:線程互斥是相當於兩個寫者同時去寫一個檔案,造成的混亂;線程同步相當於當有讀者在讀檔案的時候,寫者還在不停的寫造成的混亂。在以前的基礎上,我們把線程ID傳進去。(主線程去寫,子線程去讀)。
沒有用事件的情況:
#include <stdio.h>#include <process.h>#include <windows.h>long g_nNum;unsigned int __stdcall Fun(void *pPM);const int THREAD_NUM = 10;CRITICAL_SECTION g_csThreadCode;int main(){InitializeCriticalSection(&g_csThreadCode);HANDLE handle[THREAD_NUM];g_nNum = 0;int i = 0;while (i < THREAD_NUM) {handle[i++] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);}WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);for(i=0;i<sizeof(handle);i++){CloseHandle(handle[i]);}DeleteCriticalSection(&g_csThreadCode);return 0;}unsigned int __stdcall Fun(void *pPM){int nThreadNum = *(int *)pPM; Sleep(50);EnterCriticalSection(&g_csThreadCode);g_nNum++;Sleep(0);printf("當前線程為:%d,當前計數為:%d\n",nThreadNum,g_nNum);LeaveCriticalSection(&g_csThreadCode);return 0;}
線程ID不符合要求。。
使用事件的情況:(下面這裡用的是手動設定事件,所以要調用ResetEvent)。
#include <stdio.h>#include <process.h>#include <windows.h>long g_nNum;unsigned int __stdcall Fun(void *pPM);const int THREAD_NUM = 10;HANDLE g_hThreadEvent;CRITICAL_SECTION g_csThreadCode;int main(){g_hThreadEvent = CreateEvent(NULL, TRUE, FALSE, NULL); InitializeCriticalSection(&g_csThreadCode);HANDLE handle[THREAD_NUM];g_nNum = 0;int i = 0;while (i < THREAD_NUM) {handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);WaitForSingleObject(g_hThreadEvent, INFINITE); ResetEvent(g_hThreadEvent);i++;}WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);for(i=0;i<sizeof(handle);i++){CloseHandle(handle[i]);}CloseHandle(g_hThreadEvent);DeleteCriticalSection(&g_csThreadCode);return 0;}unsigned int __stdcall Fun(void *pPM){int nThreadNum = *(int *)pPM; SetEvent(g_hThreadEvent);Sleep(50);EnterCriticalSection(&g_csThreadCode);g_nNum++;Sleep(0);printf("當前線程為:%d,當前計數為:%d\n",nThreadNum,g_nNum);LeaveCriticalSection(&g_csThreadCode);return 0;}
線程ID符合要求。。
互斥量
#include <stdio.h>#include <process.h>#include <windows.h>long g_nNum;unsigned int __stdcall Fun(void *pPM);const int THREAD_NUM = 10;HANDLE g_mutex;int main(){g_mutex = CreateMutex(NULL, FALSE, NULL);HANDLE handle[THREAD_NUM];g_nNum = 0;int i = 0;while (i < THREAD_NUM) {handle[i++] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL); }WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);CloseHandle(g_mutex);for (i = 0; i < THREAD_NUM; i++)CloseHandle(handle[i]);return 0;}unsigned int __stdcall Fun(void *pPM){Sleep(50);WaitForSingleObject(g_mutex,INFINITE);g_nNum++;Sleep(0);printf("當前計數為:%d\n",g_nNum);ReleaseMutex(g_mutex);return 0;}
訊號量(Semaphore)
HANDLE
CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
LONG lInitialCount,
LONG lMaximumCount,
LPCTSTR lpName
);------------------建立訊號量
HANDLE
OpenSemaphore(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
);------------------開啟訊號量
BOOL
ReleaseSemaphore(
HANDLE hSemaphore,
LONG lReleaseCount,
LPLONG lpPreviousCount
);--------------------釋放訊號量
訊號量的增:ReleaseSemaphore。訊號量的減:WaitForSingleObject(..);當請求訊號量時,如果當前訊號量為0即不觸發狀態,那麼線程進入阻塞狀態,直到訊號量值>0,該函數才返回,線程進入可調度狀態,同時訊號量值減一。
注意:訊號量的值不可能小於0。
#include <stdio.h>#include <process.h>#include <windows.h>long g_nNum;unsigned int __stdcall Fun(void *pPM);const int THREAD_NUM = 10;HANDLE g_Semaphore;CRITICAL_SECTION g_CriticalSection;int main(){g_Semaphore = CreateSemaphore(NULL, 0, 1, NULL);InitializeCriticalSection(&g_CriticalSection);HANDLE handle[THREAD_NUM];g_nNum = 0;int i = 0;while (i < THREAD_NUM) {handle[i] = (HANDLE)_beginthreadex(NULL, 0, Fun, &i, 0, NULL);WaitForSingleObject(g_Semaphore, INFINITE);++i;}WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE);DeleteCriticalSection(&g_CriticalSection);CloseHandle(g_Semaphore);for (i = 0; i < THREAD_NUM; i++)CloseHandle(handle[i]);return 0;}unsigned int __stdcall Fun(void *pPM){int nThreadNum = *(int *)pPM;ReleaseSemaphore(g_Semaphore, 1, NULL);Sleep(50);EnterCriticalSection(&g_CriticalSection);++g_nNum;Sleep(0);printf("當前線程為:%d,當前計數為:%d\n",nThreadNum,g_nNum);LeaveCriticalSection(&g_CriticalSection);return 0;}