C++ 如何實現(xiàn)多線程與線程同步
CreateThread 實現(xiàn)多線程:
先來創(chuàng)建一個簡單的多線程實例,無參數(shù)傳遞版,運行實例會發(fā)現(xiàn),主線程與子線程運行無規(guī)律。
#include <windows.h> #include <iostream> using namespace std; DWORD WINAPI Func(LPVOID lpParamter) { for (int x = 0; x < 10; x++) { cout << "thread function" << endl; Sleep(200); } return 0; } int main(int argc,char * argv[]) { HANDLE hThread = CreateThread(NULL, 0, Func, NULL, 0, NULL); CloseHandle(hThread); for (int x = 0; x < 10; x++) { cout << "main thread" << endl; Sleep(400); } system("pause"); return 0; }
beginthreadex 實現(xiàn)多線程:
這個方法與前面的CreateThread使用完全一致,只是在參數(shù)上面應使用void *該參數(shù)可以強轉(zhuǎn)為任意類型,兩者實現(xiàn)效果完全一致。
#include <windows.h> #include <iostream> #include <process.h> using namespace std; unsigned WINAPI Func(void *arg) { for (int x = 0; x < 10; x++) { cout << "thread function" << endl; Sleep(200); } return 0; } int main(int argc, char * argv[]) { HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, Func, NULL, 0, NULL); CloseHandle(hThread); for (int x = 0; x < 10; x++) { cout << "main thread" << endl; Sleep(400); } system("pause"); return 0; }
CreateMutex 互斥鎖實現(xiàn)線程同步:
使用互斥鎖可以實現(xiàn)單位時間內(nèi),只允許一個線程擁有對共享資源的獨占,從而實現(xiàn)了互不沖突的線程同步。
#include <windows.h> #include <iostream> using namespace std; HANDLE hMutex = NULL; // 創(chuàng)建互斥鎖 // 線程函數(shù) DWORD WINAPI Func(LPVOID lpParamter) { for (int x = 0; x < 10; x++) { // 請求獲得一個互斥鎖 WaitForSingleObject(hMutex, INFINITE); cout << "thread func" << endl; // 釋放互斥鎖 ReleaseMutex(hMutex); } return 0; } int main(int argc,char * argv[]) { HANDLE hThread = CreateThread(NULL, 0, Func, NULL, 0, NULL); hMutex = CreateMutex(NULL, FALSE, "lyshark"); CloseHandle(hThread); for (int x = 0; x < 10; x++) { // 請求獲得一個互斥鎖 WaitForSingleObject(hMutex, INFINITE); cout << "main thread" << endl; // 釋放互斥鎖 ReleaseMutex(hMutex); } system("pause"); return 0; }
通過互斥鎖,同步執(zhí)行兩個線程函數(shù)。
#include <windows.h> #include <iostream> using namespace std; HANDLE hMutex = NULL; // 創(chuàng)建互斥鎖 #define NUM_THREAD 50 // 線程函數(shù)1 DWORD WINAPI FuncA(LPVOID lpParamter) { for (int x = 0; x < 10; x++) { // 請求獲得一個互斥鎖 WaitForSingleObject(hMutex, INFINITE); cout << "this is thread func A" << endl; // 釋放互斥鎖 ReleaseMutex(hMutex); } return 0; } // 線程函數(shù)2 DWORD WINAPI FuncB(LPVOID lpParamter) { for (int x = 0; x < 10; x++) { // 請求獲得一個互斥鎖 WaitForSingleObject(hMutex, INFINITE); cout << "this is thread func B" << endl; // 釋放互斥鎖 ReleaseMutex(hMutex); } return 0; } int main(int argc, char * argv[]) { // 用來存儲線程函數(shù)的句柄 HANDLE tHandle[NUM_THREAD]; // /創(chuàng)建互斥量,此時為signaled狀態(tài) hMutex = CreateMutex(NULL, FALSE, "lyshark"); for (int x = 0; x < NUM_THREAD; x++) { if (x % 2) { tHandle[x] = CreateThread(NULL, 0, FuncA, NULL, 0, NULL); } else { tHandle[x] = CreateThread(NULL, 0, FuncB, NULL, 0, NULL); } } // 等待所有線程函數(shù)執(zhí)行完畢 WaitForMultipleObjects(NUM_THREAD, tHandle, TRUE, INFINITE); // 銷毀互斥對象 CloseHandle(hMutex); system("pause"); return 0; }
通過臨界區(qū)實現(xiàn)線程同步:
臨界區(qū)與互斥鎖差不多,臨界區(qū)使用時會創(chuàng)建CRITICAL_SECTION臨界區(qū)對象,同樣相當于一把鑰匙,線程函數(shù)執(zhí)行結(jié)束自動上交,如下是臨界區(qū)函數(shù)的定義原型。
//初始化函數(shù)原型 VOID InitializeCriticalSection( LPCRITICAL_SECTION lpCriticalSection ); //銷毀函數(shù)原型 VOID DeleteCriticalSection( LPCRITICAL_SECTION lpCriticalSection ); //獲取 VOID EnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection ); //釋放 VOID LeaveCriticalSection( LPCRITICAL_SECTION lpCriticalSection );
這一次我們不適用互斥體,使用臨界區(qū)實現(xiàn)線程同步,結(jié)果與互斥體完全一致,看個人喜好。
#include <windows.h> #include <iostream> using namespace std; CRITICAL_SECTION cs; // 全局定義臨界區(qū)對象 #define NUM_THREAD 50 // 線程函數(shù) DWORD WINAPI FuncA(LPVOID lpParamter) { for (int x = 0; x < 10; x++) { //進入臨界區(qū) EnterCriticalSection(&cs); cout << "this is thread func A" << endl; //離開臨界區(qū) LeaveCriticalSection(&cs); } return 0; } int main(int argc, char * argv[]) { // 用來存儲線程函數(shù)的句柄 HANDLE tHandle[NUM_THREAD]; //初始化臨界區(qū) InitializeCriticalSection(&cs); for (int x = 0; x < NUM_THREAD; x++) { tHandle[x] = CreateThread(NULL, 0, FuncA, NULL, 0, NULL); } // 等待所有線程函數(shù)執(zhí)行完畢 WaitForMultipleObjects(NUM_THREAD, tHandle, TRUE, INFINITE); //釋放臨界區(qū) DeleteCriticalSection(&cs); system("pause"); return 0; }
Semaphore 基于信號實現(xiàn)線程同步:
通過定義一個信號,初始化信號為0,利用信號量值為0時進入non-signaled狀態(tài),大于0時進入signaled狀態(tài)的特性即可實現(xiàn)線程同步。
#include <windows.h> #include <iostream> using namespace std; static HANDLE SemaphoreOne; static HANDLE SemaphoreTwo; // 線程函數(shù)1 DWORD WINAPI FuncA(LPVOID lpParamter) { for (int x = 0; x < 10; x++) { // 臨界區(qū)開始時設(shè)置 signaled 狀態(tài) WaitForSingleObject(SemaphoreOne, INFINITE); cout << "this is thread func A" << endl; // 臨界區(qū)結(jié)束則設(shè)置為 non-signaled 狀態(tài) ReleaseSemaphore(SemaphoreOne, 1, NULL); } return 0; } // 線程函數(shù)2 DWORD WINAPI FuncB(LPVOID lpParamter) { for (int x = 0; x < 10; x++) { // 臨界區(qū)開始時設(shè)置 signaled 狀態(tài) WaitForSingleObject(SemaphoreTwo, INFINITE); cout << "this is thread func B" << endl; // 臨界區(qū)結(jié)束則設(shè)置為 non-signaled 狀態(tài) ReleaseSemaphore(SemaphoreTwo, 1, NULL); } return 0; } int main(int argc, char * argv[]) { // 用來存儲線程函數(shù)的句柄 HANDLE hThreadA, hThreadB; // 創(chuàng)建信號量對象,并且設(shè)置為0進入non-signaled狀態(tài) SemaphoreOne = CreateSemaphore(NULL, 0, 1, NULL); // 創(chuàng)建信號量對象,并且設(shè)置為1進入signaled狀態(tài) SemaphoreTwo = CreateSemaphore(NULL, 1, 1, NULL); // 先執(zhí)行這一個線程函數(shù) hThreadA = CreateThread(NULL, 0, FuncA, NULL,0, NULL); hThreadB = CreateThread(NULL, 0, FuncB, NULL, 0, NULL); // 等待兩個線程函數(shù)執(zhí)行完畢 WaitForSingleObject(hThreadA, INFINITE); WaitForSingleObject(hThreadA, INFINITE); // 銷毀兩個線程函數(shù) CloseHandle(SemaphoreOne); CloseHandle(SemaphoreTwo); system("pause"); return 0; }
上面的一段代碼,容易產(chǎn)生死鎖現(xiàn)象,即,線程函數(shù)B執(zhí)行完成后,A函數(shù)一直處于等待狀態(tài)。
執(zhí)行WaitForSingleObject(semTwo, INFINITE);會讓線程函數(shù)進入類似掛起的狀態(tài),當接到ReleaseSemaphore(semOne, 1, NULL);才會恢復執(zhí)行。
#include <windows.h> #include <stdio.h> static HANDLE semOne,semTwo; static int num; // 線程函數(shù)A用于接收參書 DWORD WINAPI ReadNumber(LPVOID lpParamter) { int i; for (i = 0; i < 5; i++) { fputs("Input Number: ", stdout); //臨界區(qū)的開始 signaled狀態(tài) WaitForSingleObject(semTwo, INFINITE); scanf("%d", &num); //臨界區(qū)的結(jié)束 non-signaled狀態(tài) ReleaseSemaphore(semOne, 1, NULL); } return 0; } // 線程函數(shù)B: 用戶接受參數(shù)后完成計算 DWORD WINAPI Check(LPVOID lpParamter) { int sum = 0, i; for (i = 0; i < 5; i++) { //臨界區(qū)的開始 non-signaled狀態(tài) WaitForSingleObject(semOne, INFINITE); sum += num; //臨界區(qū)的結(jié)束 signaled狀態(tài) ReleaseSemaphore(semTwo, 1, NULL); } printf("The Number IS: %d \n", sum); return 0; } int main(int argc, char *argv[]) { HANDLE hThread1, hThread2; //創(chuàng)建信號量對象,設(shè)置為0進入non-signaled狀態(tài) semOne = CreateSemaphore(NULL, 0, 1, NULL); //創(chuàng)建信號量對象,設(shè)置為1進入signaled狀態(tài) semTwo = CreateSemaphore(NULL, 1, 1, NULL); hThread1 = CreateThread(NULL, 0, ReadNumber, NULL, 0, NULL); hThread2 = CreateThread(NULL, 0, Check, NULL, 0, NULL); // 關(guān)閉臨界區(qū) WaitForSingleObject(hThread1, INFINITE); WaitForSingleObject(hThread2, INFINITE); CloseHandle(semOne); CloseHandle(semTwo); system("pause"); return 0; }
CreateEvent 事件對象的同步:
事件對象實現(xiàn)線程同步,與前面的臨界區(qū)和互斥體有很大的不同,該方法下創(chuàng)建對象時,可以在自動non-signaled狀態(tài)運行的auto-reset模式,當我們設(shè)置好我們需要的參數(shù)時,可以直接使用SetEvent(hEvent)設(shè)置事件狀態(tài),會自動執(zhí)行線程函數(shù)。
#include <windows.h> #include <stdio.h> #include <process.h> #define STR_LEN 100 // 存儲全局字符串 static char str[STR_LEN]; // 設(shè)置事件句柄 static HANDLE hEvent; // 統(tǒng)計字符串中是否存在A unsigned WINAPI NumberOfA(void *arg) { int cnt = 0; // 等待線程對象事件 WaitForSingleObject(hEvent, INFINITE); for (int i = 0; str[i] != 0; i++) { if (str[i] == 'A') cnt++; } printf("Num of A: %d \n", cnt); return 0; } // 統(tǒng)計字符串總長度 unsigned WINAPI NumberOfOthers(void *arg) { int cnt = 0; // 等待線程對象事件 WaitForSingleObject(hEvent, INFINITE); for (int i = 0; str[i] != 0; i++) { if (str[i] != 'A') cnt++; } printf("Num of others: %d \n", cnt - 1); return 0; } int main(int argc, char *argv[]) { HANDLE hThread1, hThread2; // 以non-signaled創(chuàng)建manual-reset模式的事件對象 // 該對象創(chuàng)建后不會被立即執(zhí)行,只有我們設(shè)置狀態(tài)為Signaled時才會繼續(xù) hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); hThread1 = (HANDLE)_beginthreadex(NULL, 0, NumberOfA, NULL, 0, NULL); hThread2 = (HANDLE)_beginthreadex(NULL, 0, NumberOfOthers, NULL, 0, NULL); fputs("Input string: ", stdout); fgets(str, STR_LEN, stdin); // 字符串讀入完畢后,將事件句柄改為signaled狀態(tài) SetEvent(hEvent); WaitForSingleObject(hThread1, INFINITE); WaitForSingleObject(hThread2, INFINITE); //non-signaled 如果不更改,對象繼續(xù)停留在signaled ResetEvent(hEvent); CloseHandle(hEvent); system("pause"); return 0; }
線程函數(shù)傳遞單個參數(shù):
線程函數(shù)中的定義中LPVOID允許傳遞一個參數(shù),只需要在縣城函數(shù)中接收并強轉(zhuǎn)(int)(LPVOID)port即可。
#include <stdio.h> #include <Windows.h> // 線程函數(shù)接收一個參數(shù) DWORD WINAPI ScanThread(LPVOID port) { // 將參數(shù)強制轉(zhuǎn)化為需要的類型 int Port = (int)(LPVOID)port; printf("[+] 端口: %5d \n", port); return 1; } int main(int argc, char* argv[]) { HANDLE handle; for (int port = 0; port < 100; port++) { handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ScanThread, (LPVOID)port, 0, 0); } WaitForSingleObject(handle, INFINITE); system("pause"); return 0; }
線程函數(shù)傳遞多參數(shù):
如果想在線程函數(shù)中傳遞多個參數(shù),則需要傳遞一個結(jié)構(gòu)指針,通過線程函數(shù)內(nèi)部強轉(zhuǎn)為結(jié)構(gòu)類型后,取值,這個案例花費了我一些時間,網(wǎng)上也沒找到合適的解決方法,或找到的都是歪瓜裂棗瞎轉(zhuǎn)的東西,最后還是自己研究了一下寫了一個沒為題的。
其主要是線程函數(shù)中調(diào)用的參數(shù)會與下一個線程函數(shù)結(jié)構(gòu)相沖突,解決的辦法時在每次進入線程函數(shù)時,自己拷貝一份,每個人使用自己的那一份,才可以避免此類事件的發(fā)生,同時最好配合線程同步一起使用,如下時線程掃描器的部分代碼片段。
#include <stdio.h> #include <windows.h> typedef struct _THREAD_PARAM { char *HostAddr; // 掃描主機 DWORD dwStartPort; // 端口號 }THREAD_PARAM; // 這個掃描線程函數(shù) DWORD WINAPI ScanThread(LPVOID lpParam) { // 拷貝傳遞來的掃描參數(shù) THREAD_PARAM ScanParam = { 0 }; // 這一步很重要,如不拷貝,則會發(fā)生重復賦值現(xiàn)象,導致掃描端口一直都是一個。 // 坑死人的玩意,一開始我始終沒有發(fā)現(xiàn)這個問題。sb玩意!! MoveMemory(&ScanParam, lpParam, sizeof(THREAD_PARAM)); printf("地址: %-16s --> 端口: %-5d 狀態(tài): [Open] \n", ScanParam.HostAddr, ScanParam.dwStartPort); return 0; } int main(int argc, char *argv[]) { THREAD_PARAM ThreadParam = { 0 }; ThreadParam.HostAddr = "192.168.1.10"; for (DWORD port = 1; port < 100; port++) { ThreadParam.dwStartPort = port; HANDLE hThread = CreateThread(NULL, 0, ScanThread, (LPVOID)&ThreadParam, 0, NULL); WaitForSingleObject(hThread, INFINITE); } system("pause"); return 0; }
文章出處:https://www.cnblogs.com/lyshark
以上就是C++ 如何實現(xiàn)多線程與線程同步的詳細內(nèi)容,更多關(guān)于C++ 實現(xiàn)多線程與線程同步的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C語言循環(huán)隊列與用隊列實現(xiàn)棧問題解析
循環(huán)隊列又叫環(huán)形隊列,是一種特殊的隊列。循環(huán)隊列解決了隊列出隊時需要將所有數(shù)據(jù)前移一位的問題,本篇帶你一起看看循環(huán)隊列的問題和怎樣用隊列實現(xiàn)棧2022-04-04