我们都知道,windows是一个抢占式的多线程环境,因此系统可能会在任意时刻暂停一个线程,切换到另一个线程让新的线程继续执行。请看这段代码:
// Interlocked.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <windows.h> #include <iostream> #include <process.h> using namespace std; DWORD WINAPI ThreadFunc1(PVOID pvParam); DWORD WINAPI ThreadFunc2(PVOID pvParam); long g_x = 0; int _tmain(int argc, _TCHAR* argv[]) { //创建线程1,并挂起 HANDLE hThread1 = (HANDLE)_beginthreadex(NULL,0,(unsigned int(_stdcall *)(void *)) ThreadFunc1,NULL ,0 ,0); HANDLE hThread2 = (HANDLE)_beginthreadex(NULL,0,(unsigned int(_stdcall *)(void *)) ThreadFunc2,NULL,0,0); cout<<"主线程"<<endl; Sleep(10000); cout<<g_x<<endl; CloseHandle(hThread1); CloasHandle(hThread2); system("pause"); return 0; } DWORD WINAPI ThreadFunc1(PVOID pvParam) { for(int i = 0; i<10000000;++i) { g_x++; } return 0; } DWORD WINAPI ThreadFunc2(PVOID pvParam) { for(int i = 0; i<10000000;++i) { g_x++; } return 0; } 2个线程函数,各有一个for循环,1000W次,那么,我们或许会认为,最终输出的 g_x的值会是 1000W+1000W = 20000000?
但是,在多线程环境下,是不会达到我们想要的效果的,代码可能不会严格按照我们想要的顺序执行,但线程函数1的一个时间片用完,它或许会去执行线程函数2
汇编代码是这样的:
MOV EAX,[g_x]
INC EAX
MOVE EAX,[g_x]
INC EAX
MOV [g_x],EAX
MOV [g_x],EAX
是的,有可能某次或者某几次的 g_x++的操作会被分解,打断(解释下:INC EAX 以后,没有按照预想的继续执行 MOV [g_x],EAX,而是跳转到另外的一个线程,去执行 MOVE EAX,[g_x])
因此,我们需要有一种方法能够保证对一个值的递增操作是原子操作:也就是说,操作不会被打断
我们可以用如下函数
LONG __cdecl InterlockedExchangeAdd( __in_out LONG volatile* Addend, __in LONG Value );
修改后的代码如下:
// Interlocked.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <windows.h> #include <iostream> #include <process.h> using namespace std; DWORD WINAPI ThreadFunc1(PVOID pvParam); DWORD WINAPI ThreadFunc2(PVOID pvParam); long g_x = 0; int _tmain(int argc, _TCHAR* argv[]) { //创建线程1,并挂起 HANDLE hThread1 = (HANDLE)_beginthreadex(NULL,0,(unsigned int(_stdcall *)(void *)) ThreadFunc1,NULL ,0 ,0); HANDLE hThread2 = (HANDLE)_beginthreadex(NULL,0,(unsigned int(_stdcall *)(void *)) ThreadFunc2,NULL,0,0); cout<<"主线程"<<endl; Sleep(10000); cout<<g_x<<endl; CloseHandle(hThread1); CloseHandle(hThread2); system("pause"); return 0; } DWORD WINAPI ThreadFunc1(PVOID pvParam) { for(int i = 0; i<10000000;++i) { //g_x++; InterlockedExchangeAdd(&g_x,1); } return 0; } DWORD WINAPI ThreadFunc2(PVOID pvParam) { for(int i = 0; i<10000000;++i) { //g_x++; InterlockedExchangeAdd(&g_x,1); } return 0; }
这样,我们就可以得到正确的结果了!2000W