多线程中的线程同步可以使用,CreateThread,CreateMutex 互斥锁实现线程同步,通过临界区实现线程同步,Semaphore 基于信号实现线程同步,CreateEvent 事件对象的同步,以及线程函数传递单一参数与多个参数的实现方式。
CreateThread 实现多线程: 先来创建一个简单的多线程实例,无参数传递版,运行实例会发现,主线程与子线程运行无规律。
#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 实现多线程: 这个方法与前面的CreateThread使用完全一致,只是在参数上面应使用void *
该参数可以强转为任意类型,两者实现效果完全一致。
#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 互斥锁实现线程同步: 使用互斥锁可以实现单位时间内,只允许一个线程拥有对共享资源的独占,从而实现了互不冲突的线程同步。
#include <windows.h> #include <iostream>
using namespace std; HANDLE hMutex = NULL;
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; }
|

通过互斥锁,同步执行两个线程函数。
#include <windows.h> #include <iostream>
using namespace std; HANDLE hMutex = NULL; #define NUM_THREAD 50
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; }
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[]) {
HANDLE tHandle[NUM_THREAD];
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); } }
WaitForMultipleObjects(NUM_THREAD, tHandle, TRUE, INFINITE); CloseHandle(hMutex);
system("pause"); return 0; }
|

通过临界区实现线程同步: 临界区与互斥锁差不多,临界区使用时会创建CRITICAL_SECTION临界区对象,同样相当于一把钥匙,线程函数执行结束自动上交,如下是临界区函数的定义原型。
VOID InitializeCriticalSection( LPCRITICAL_SECTION lpCriticalSection );
VOID DeleteCriticalSection( LPCRITICAL_SECTION lpCriticalSection );
VOID EnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection );
VOID LeaveCriticalSection( LPCRITICAL_SECTION lpCriticalSection );
|
这一次我们不适用互斥体,使用临界区实现线程同步,结果与互斥体完全一致,看个人喜好。
#include <windows.h> #include <iostream>
using namespace std; CRITICAL_SECTION cs; #define NUM_THREAD 50
DWORD WINAPI FuncA(LPVOID lpParamter) { for (int x = 0; x < 10; x++) { EnterCriticalSection(&cs);
cout << "this is thread func A" << endl;
LeaveCriticalSection(&cs);
} return 0; }
int main(int argc, char * argv[]) { HANDLE tHandle[NUM_THREAD];
InitializeCriticalSection(&cs);
for (int x = 0; x < NUM_THREAD; x++) { tHandle[x] = CreateThread(NULL, 0, FuncA, NULL, 0, NULL); }
WaitForMultipleObjects(NUM_THREAD, tHandle, TRUE, INFINITE); DeleteCriticalSection(&cs);
system("pause"); return 0; }
|

Semaphore 基于信号实现线程同步: 通过定义一个信号,初始化信号为0,利用信号量值为0时进入non-signaled状态,大于0时进入signaled状态的特性即可实现线程同步。
#include <windows.h> #include <iostream>
using namespace std;
static HANDLE SemaphoreOne; static HANDLE SemaphoreTwo;
DWORD WINAPI FuncA(LPVOID lpParamter) { for (int x = 0; x < 10; x++) { WaitForSingleObject(SemaphoreOne, INFINITE);
cout << "this is thread func A" << endl;
ReleaseSemaphore(SemaphoreOne, 1, NULL); } return 0; }
DWORD WINAPI FuncB(LPVOID lpParamter) { for (int x = 0; x < 10; x++) { WaitForSingleObject(SemaphoreTwo, INFINITE);
cout << "this is thread func B" << endl;
ReleaseSemaphore(SemaphoreTwo, 1, NULL); } return 0; }
int main(int argc, char * argv[]) { HANDLE hThreadA, hThreadB;
SemaphoreOne = CreateSemaphore(NULL, 0, 1, NULL);
SemaphoreTwo = CreateSemaphore(NULL, 1, 1, NULL);
hThreadA = CreateThread(NULL, 0, FuncA, NULL,0, NULL); hThreadB = CreateThread(NULL, 0, FuncB, NULL, 0, NULL);
WaitForSingleObject(hThreadA, INFINITE); WaitForSingleObject(hThreadA, INFINITE);
CloseHandle(SemaphoreOne); CloseHandle(SemaphoreTwo);
system("pause"); return 0; }
|

上面的一段代码,容易产生死锁现象,即,线程函数B执行完成后,A函数一直处于等待状态。
执行WaitForSingleObject(semTwo, INFINITE);
会让线程函数进入类似挂起的状态,当接到ReleaseSemaphore(semOne, 1, NULL);
才会恢复执行。
#include <windows.h> #include <stdio.h>
static HANDLE semOne,semTwo; static int num;
DWORD WINAPI ReadNumber(LPVOID lpParamter) { int i; for (i = 0; i < 5; i++) { fputs("Input Number: ", stdout); WaitForSingleObject(semTwo, INFINITE); scanf("%d", &num);
ReleaseSemaphore(semOne, 1, NULL); } return 0; }
DWORD WINAPI Check(LPVOID lpParamter) { int sum = 0, i; for (i = 0; i < 5; i++) { WaitForSingleObject(semOne, INFINITE); sum += num; ReleaseSemaphore(semTwo, 1, NULL); } printf("The Number IS: %d \n", sum); return 0; }
int main(int argc, char *argv[]) { HANDLE hThread1, hThread2;
semOne = CreateSemaphore(NULL, 0, 1, NULL);
semTwo = CreateSemaphore(NULL, 1, 1, NULL);
hThread1 = CreateThread(NULL, 0, ReadNumber, NULL, 0, NULL); hThread2 = CreateThread(NULL, 0, Check, NULL, 0, NULL);
WaitForSingleObject(hThread1, INFINITE); WaitForSingleObject(hThread2, INFINITE);
CloseHandle(semOne); CloseHandle(semTwo);
system("pause"); return 0; }
|

CreateEvent 事件对象的同步: 事件对象实现线程同步,与前面的临界区和互斥体有很大的不同,该方法下创建对象时,可以在自动non-signaled状态运行的auto-reset模式,当我们设置好我们需要的参数时,可以直接使用SetEvent(hEvent)
设置事件状态,会自动执行线程函数。
#include <windows.h> #include <stdio.h> #include <process.h> #define STR_LEN 100
static char str[STR_LEN];
static HANDLE hEvent;
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; }
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;
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);
SetEvent(hEvent);
WaitForSingleObject(hThread1, INFINITE); WaitForSingleObject(hThread2, INFINITE);
ResetEvent(hEvent);
CloseHandle(hEvent);
system("pause"); return 0; }
|

线程函数传递单个参数: 线程函数中的定义中LPVOID
允许传递一个参数,只需要在县城函数中接收并强转(int)(LPVOID)port
即可。
#include <stdio.h> #include <Windows.h>
DWORD WINAPI ScanThread(LPVOID port) { 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; }
|

线程函数传递多参数: 如果想在线程函数中传递多个参数,则需要传递一个结构指针,通过线程函数内部强转为结构类型后,取值,这个案例花费了我一些时间,网上也没找到合适的解决方法,或找到的都是歪瓜裂枣瞎转的东西,最后还是自己研究了一下写了一个没为题的。
其主要是线程函数中调用的参数会与下一个线程函数结构相冲突,解决的办法时在每次进入线程函数时,自己拷贝一份,每个人使用自己的那一份,才可以避免此类事件的发生,同时最好配合线程同步一起使用,如下时线程扫描器的部分代码片段。
#include <stdio.h> #include <windows.h>
typedef struct _THREAD_PARAM { char *HostAddr; DWORD dwStartPort; }THREAD_PARAM;
DWORD WINAPI ScanThread(LPVOID lpParam) { THREAD_PARAM ScanParam = { 0 };
MoveMemory(&ScanParam, lpParam, sizeof(THREAD_PARAM));
printf("地址: %-16s --> 端口: %-5d 状态: [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; }
|
