1.CreateThread与_beginthreadex
#pragma once #include<cstdio> #include<Windows.h> #include<crtdbg.h> #include<process.h> //子线程函数 DWORD WINAPI ThreadFun1(LPVOID pM) { printf("子线程的线程ID号为:%d\nHello world!\n",GetCurrentThreadId()); return 0; } void fun1() { printf("简单多线程实例!\n\n"); /* CreateThread参数解析 1:线程内核安全属性 2:线程栈空间大小 3:线程执行函数地址 4:传给线程执行函数参数 5:线程创建控制参数(CREATE_SUSPENDED) 6:线程ID号 */ HANDLE handle = CreateThread(NULL, 0, ThreadFun1, NULL, 0, NULL); WaitForSingleObject(handle, INFINITE); CloseHandle(handle); } //设置计数全局变量 int COUNT = 0; //子线程函数 unsigned int _stdcall ThreadFun2(PVOID pM) { ++COUNT; printf("子线程的线程ID号为:%d,报数为%d\nHello world!\n", GetCurrentThreadId(),COUNT); return 0; } void fun2() { printf("简单多线程实例!\n\n"); const int THREAD_NUM = 5; HANDLE handle[THREAD_NUM]; for (size_t i = 0; i < THREAD_NUM; i++) { handle[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadFun2, NULL, 0, NULL); } WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE); } int main(void) { //使用CreateThread //fun1(); //使用_beginthreadex //推荐使用原因为:使用标准C运行库函数时,易发生race condition //使用_beginthreadex可以避免数据被其他线程篡改 //更加合理的解释可以参考Win32多线程编程 fun2(); //检测内存泄漏 _CrtDumpMemoryLeaks(); return 0; }
其中执行fun2结果为:(蛮有趣的,不加锁竟然这么直观)
2.原子操作
#pragma once #include<cstdio> #include<Windows.h> #include<crtdbg.h> #include<process.h> volatile long COUNT = 0; const int THREAD_NUM = 500; unsigned int _stdcall ThreadFun(LPVOID pM) { Sleep(50); //++COUNT; InterlockedIncrement((LPLONG)&COUNT); //使用原子锁替换 Sleep(50); return 0; } void fun1() { HANDLE handle[THREAD_NUM]; for (size_t i = 0; i < THREAD_NUM; i++) { handle[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadFun, NULL, 0, NULL); } WaitForMultipleObjects(THREAD_NUM, handle, TRUE, INFINITE); printf("有%d个线程启动,记录结果为%d", THREAD_NUM, COUNT); } int main() { //test1 fun1(); //易发现线程启动数和计数不匹配 //检测内存泄漏 _CrtDumpMemoryLeaks(); return 0; }
这里++操作在汇编层面是分成三层的:(1)取值由内存存至寄存器;(2)寄存器中进行操作;(3)数值由寄存器转储至内存。这个过程容易出现问题。
但是使用原子操作在只有50个线程启动时准确,但是上限调至500次时,线程启动数和计数又不一致。
时间: 2024-10-10 13:19:50