多線程編程--5種方法實現線程同步
2:用CRITICAL_SECTION及其系列函數實現線程同步;
1:用Interlocked系列函數實現線程同步實例如下:
//旋轉鎖 #include <iostream> using namespace std; #include <process.h> #include <windows.h> const int threadNum=10; HANDLE hThread[threadNum]; volatile unsigned int ISOK=0; unsigned int _stdcall Interlocked(PVOID threadId) { while(InterlockedExchange(&ISOK,1)==1) ; cout<<"線程:"<<*(int*)threadId<<"開始"<<endl; Sleep(100); cout<<"線程:"<<*(int*)threadId<<"結束"<<endl; InterlockedExchange(&ISOK,0); return 0; } void InterlockedTest() { int threadId[threadNum]; for(int i=0;i<threadNum;i++) { threadId[i]=i+1; } cout<<"1:用Interlocked系列函數實現線程同步"<<endl; for(int i=0;i<10;i++){ hThread[i]=(HANDLE)_beginthreadex(NULL, 0, Interlocked,threadId+i, 0, NULL); } WaitForMultipleObjects(threadNum, hThread, TRUE, INFINITE); for(int i=0;i<threadNum;i++) { CloseHandle(hThread[i]); } }

InterlockedExchange確保以原子的方式操作數據。執行速度非常快,缺點是如果要同步的部分執行的時間比較長的話,while循環會一直輪詢操作,浪費CPU的時間,在單核CPU的系統中,可能會出現while一直暫用CPU導致其他線程不能修改ISOK的值,導致不能跳出while循環,出現死循環。還有就是線程的優先級問題也能導致問題。
2:用CRITICAL_SECTION及其系列函數實現線程同步實例如下:
//關鍵段 #include <iostream> using namespace std; #include <process.h> #include <windows.h> const int threadNum=10; HANDLE hThread[threadNum]; CRITICAL_SECTION g_cs;//構造一個CRITICAL_SECTION實例 unsigned int _stdcall CriticalSection(PVOID threadId) { EnterCriticalSection(&g_cs);//進入關鍵段 cout<<"線程:"<<*(int*)threadId<<"開始"<<endl; Sleep(100); cout<<"線程:"<<*(int*)threadId<<"結束"<<endl; LeaveCriticalSection(&g_cs);//進入關鍵段 return 0; } void CriticalSectionTest() { int threadId[threadNum]; for(int i=0;i<threadNum;i++) { threadId[i]=i+1; } InitializeCriticalSection(&g_cs);//初始化g_cs的成員 cout<<"2:用CRITICAL_SECTION及其系列函數實現線程同步"<<endl; for(int i=0;i<10;i++){ hThread[i]=(HANDLE)_beginthreadex(NULL, 0, CriticalSection,threadId+i, 0, NULL); } WaitForMultipleObjects(threadNum, hThread, TRUE, INFINITE); for(int i=0;i<threadNum;i++) { CloseHandle(hThread[i]); } DeleteCriticalSection(&g_cs);//刪除關鍵段 }

CRITICAL_SECTION同樣是以原子的方式操作數據,也只有以原子的方式操作數據才能實現線程的同步,所有實現線程同步的方法,最核心的部分就是以原子的方式操作數據,CRITICAL_SECTION執行的速度非常快,其內部有一個事件內核對象,當出現資源爭奪的時候,才會出現初始化這個事件內核對象,由于CRITICAL_SECTION執行非常快可能不會出現資源爭奪,也就沒有必要創建這個事件內核對象,這個事件內核對象創建后,會將當前線程之外的線程掛起,并記錄這些線程需要這個資源,其他線程就不會浪費CPU的時間,而這些被掛起的線程將由用戶模式變成內核模式,當這些線程需要的資源可用時,系統會將其中一個線程喚醒。
還有一點值得注意:如果要同步的代碼執行得很快,在出現爭奪資源的時候,系統把其他線程掛起,而當前線程又馬上執行完成了,系統又將掛起的線程喚醒,這個過程是非常浪費CPU的,也影響程序的性能,為了避免這種情況,可以結合旋轉鎖和CRITICAL_SECTION,先用旋轉鎖輪詢一定次數,還不能獲得資源,再將線程掛起,等待資源被釋放,系統再將線程喚醒,實現這一功能的就是方法
InitializeCriticalSectionAndSpinCount(
LPCRITICAL_SECTION lpCriticalSection,
DWORD dwSpinCount//旋轉鎖輪詢的次數
);
除了初始化CRITICAL_SECTION用的是方法InitializeCriticalSectionAndSpinCount,而不是方法InitializeCriticalSection,其他的都是一樣的。
3:用RTL_SRWLOCK及其系列函數實現線程同步實例如下:
//讀寫鎖 #include <iostream> using namespace std; #include <process.h> #include <windows.h> const int threadNum=10; HANDLE hThread[threadNum]; RTL_SRWLOCK lock;//構造一個CRITICAL_SECTION實例 unsigned int _stdcall SrwLock(PVOID threadId) { AcquireSRWLockExclusive(&lock);//進入讀寫鎖 cout<<"線程:"<<*(int*)threadId<<"開始"<<endl; Sleep(100); cout<<"線程:"<<*(int*)threadId<<"結束"<<endl; ReleaseSRWLockExclusive(&lock);//進入讀寫鎖 return 0; } void SrwLockTest() { int threadId[threadNum]; for(int i=0;i<threadNum;i++) { threadId[i]=i+1; } InitializeSRWLock(&lock);//初始化lock的成員 cout<<"3:用RTL_SRWLOCK及其系列函數實現線程同步"<<endl; for(int i=0;i<10;i++){ hThread[i]=(HANDLE)_beginthreadex(NULL, 0, SrwLock,threadId+i, 0, NULL); } WaitForMultipleObjects(threadNum, hThread, TRUE, INFINITE); for(int i=0;i<threadNum;i++) { CloseHandle(hThread[i]); } }

SRWLock的目的和關鍵段是一樣的,就是對資源的保護,不讓其他線程訪問。不同的是,它區分線程是讀線程還是寫線程。我們都是知道,一個資源可以同時被多個線程同時讀,就是不能同時讀,或是讀寫。也是是說寫必須是獨占的方式,而讀可以以共享的方式訪問,如果以共享的方式訪問肯定就比CRITICAL_SECTION性能好。
4:用事件內核對象實現線程同步實例如下:
//事件 #include <iostream> using namespace std; #include <process.h> #include <windows.h> const int threadNum=10; HANDLE hThread[threadNum]; HANDLE event1; unsigned int _stdcall Event(PVOID threadId) { WaitForSingleObject(event1,INFINITE); int* p=(int*)threadId; cout<<"線程:"<<*p<<"開始"<<endl; Sleep(100); cout<<"線程:"<<*p<<"結束"<<endl; SetEvent(event1); return 1; } void EventTest() { int threadId[threadNum]; for(int i=0;i<threadNum;i++) { threadId[i]=i+1; } event1=CreateEvent(NULL,false,true,NULL); cout<<"4:用事件內核對象實現線程同步"<<endl; for(int i=0;i<threadNum;i++) { hThread[i] =(HANDLE)_beginthreadex(NULL, 0, Event ,threadId+i, 0, NULL); } WaitForMultipleObjects(threadNum, hThread, TRUE, INFINITE); for(int i=0;i<threadNum;i++) { CloseHandle(hThread[i]); } CloseHandle(event1); }

用內核對象實現線程同步,一個函數是必須知道的,它就是WaitForSingleObject。
DWORD WaitForSingleObject(
HANDLE hHandle,//內核對象的句柄
DWORD dwMilliseconds//等待時間
);
該函數會一直等待,直到被指定的內核對象被觸發為止,或是等待的時間結束返回。
CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes,//安全控制
BOOL bManualReset,//true:手動重置事件,false:自動重置事件
BOOL bInitialState,//true:有信號,false:無信號
LPCWSTR lpName//事件名稱
);
bManualReset為true表示事件觸發了并一直處于觸發狀態,就像打開的門,打開之后就是一直開著,沒有自動關上;false:一打開放一個進去進關了,需要用SetEvent再次觸發事件。
5:用信號量內核對象實現線程同步實例如下:
//信號量 #include <iostream> using namespace std; #include <process.h> #include <windows.h> const int threadNum=10; HANDLE hThread[threadNum]; HANDLE semaphore; unsigned int _stdcall Semaphore(PVOID threadId) { WaitForSingleObject(semaphore, INFINITE); cout<<"線程:"<<*(int*)threadId<<"開始"<<endl; Sleep(100); cout<<"線程:"<<*(int*)threadId<<"結束"<<endl; ReleaseSemaphore(semaphore,1,NULL); return 0; } void SemaphoreTest() { int threadId[threadNum]; for(int i=0;i<threadNum;i++) { threadId[i]=i+1; } semaphore=CreateSemaphore(NULL,1,1,NULL); cout<<"5:用信號量內核對象實現線程同步"<<endl; for(int i=0;i<10;i++){ hThread[i]=(HANDLE)_beginthreadex(NULL, 0, Semaphore,threadId+i, 0, NULL); } WaitForMultipleObjects(threadNum, hThread, TRUE, INFINITE); for(int i=0;i<threadNum;i++) { CloseHandle(hThread[i]); } CloseHandle(semaphore); }

信號量內核對象用來對資源進行計數。創建信號量內核對象的方法如下:
CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,//安全控制
LONG lInitialCount,//初始資源數量
LONG lMaximumCount,//最大并發數量
LPCWSTR lpName//號量的名稱
);
lMaximumCount表示最大并發數量,可以用來設置系統的最大并發數量,如果我們把他的值設為1,lInitialCount也設為1,就是只有一個資源,且每次只能一個線程訪問,這樣就可以實現線程同步。
在實現線程同步時,建議用方法2和方法3,如不能解決你的需求,再用方法4,方法5,用內核對象實現的線程同步性能要差一些。
多線程編程5中實現線程同步的方法介紹差多就到此了,大家可能還有一些疑問,可以看看我之前關于多線程基礎知識的一些介紹:
浙公網安備 33010602011771號