赞
踩
多线程中的线程同步可以使用,CreateThread,CreateMutex 互斥锁实现线程同步,通过临界区实现线程同步,Semaphore 基于信号实现线程同步,CreateEvent 事件对象的同步,以及线程函数传递单一参数与多个参数的实现方式。
先来创建一个简单的多线程实例,无参数传递版,运行实例会发现,主线程与子线程运行无规律。
- #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;
- }
这个方法与前面的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;
- }
使用互斥锁可以实现单位时间内,只允许一个线程拥有对共享资源的独占,从而实现了互不冲突的线程同步。
- #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
-
- // 线程函数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;
- }
-
- // 线程函数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[])
- {
-
- // 用来存储线程函数的句柄
- HANDLE tHandle[NUM_THREAD];
-
- // /创建互斥量,此时为signaled状态
- 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;
- }
通过定义一个信号,初始化信号为0,利用信号量值为0时进入non-signaled状态,大于0时进入signaled状态的特性即可实现线程同步。
- #include <windows.h>
- #include <iostream>
-
- using namespace std;
-
- static HANDLE SemaphoreOne;
- static HANDLE SemaphoreTwo;
-
- // 线程函数1
- DWORD WINAPI FuncA(LPVOID lpParamter)
- {
- for (int x = 0; x < 10; x++)
- {
- // 临界区开始时设置 signaled 状态
- WaitForSingleObject(SemaphoreOne, INFINITE);
-
- cout << "this is thread func A" << endl;
-
- // 临界区结束则设置为 non-signaled 状态
- ReleaseSemaphore(SemaphoreOne, 1, NULL);
- }
- return 0;
- }
-
- // 线程函数2
- DWORD WINAPI FuncB(LPVOID lpParamter)
- {
- for (int x = 0; x < 10; x++)
- {
- // 临界区开始时设置 signaled 状态
- WaitForSingleObject(SemaphoreTwo, INFINITE);
-
- cout << "this is thread func B" << endl;
-
- // 临界区结束则设置为 non-signaled 状态
- ReleaseSemaphore(SemaphoreTwo, 1, NULL);
- }
- return 0;
- }
-
- int main(int argc, char * argv[])
- {
- // 用来存储线程函数的句柄
- HANDLE hThreadA, hThreadB;
-
- // 创建信号量对象,并且设置为0进入non-signaled状态
- SemaphoreOne = CreateSemaphore(NULL, 0, 1, NULL);
-
- // 创建信号量对象,并且设置为1进入signaled状态
- 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;
-
- // 线程函数A用于接收参书
- DWORD WINAPI ReadNumber(LPVOID lpParamter)
- {
- int i;
- for (i = 0; i < 5; i++)
- {
- fputs("Input Number: ", stdout);
- //临界区的开始 signaled状态
- WaitForSingleObject(semTwo, INFINITE);
-
- scanf("%d", &num);
-
- //临界区的结束 non-signaled状态
- ReleaseSemaphore(semOne, 1, NULL);
- }
- return 0;
- }
-
- // 线程函数B: 用户接受参数后完成计算
- DWORD WINAPI Check(LPVOID lpParamter)
- {
- int sum = 0, i;
- for (i = 0; i < 5; i++)
- {
- //临界区的开始 non-signaled状态
- WaitForSingleObject(semOne, INFINITE);
- sum += num;
- //临界区的结束 signaled状态
- ReleaseSemaphore(semTwo, 1, NULL);
- }
- printf("The Number IS: %d \n", sum);
- return 0;
- }
-
- int main(int argc, char *argv[])
- {
- HANDLE hThread1, hThread2;
-
- //创建信号量对象,设置为0进入non-signaled状态
- semOne = CreateSemaphore(NULL, 0, 1, NULL);
-
- //创建信号量对象,设置为1进入signaled状态
- 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;
- }
事件对象实现线程同步,与前面的临界区和互斥体有很大的不同,该方法下创建对象时,可以在自动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;
-
- // 统计字符串中是否存在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;
- }
-
- // 统计字符串总长度
- 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创建manual-reset模式的事件对象
- // 该对象创建后不会被立即执行,只有我们设置状态为Signaled时才会继续
- 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状态
- SetEvent(hEvent);
-
- WaitForSingleObject(hThread1, INFINITE);
- WaitForSingleObject(hThread2, INFINITE);
-
- //non-signaled 如果不更改,对象继续停留在signaled
- 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 };
-
- // 这一步很重要,如不拷贝,则会发生重复赋值现象,导致扫描端口一直都是一个。
- // 坑死人的玩意,一开始我始终没有发现这个问题。sb玩意!!
- 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;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。