原子访问--- Interlocked系列函数

我们都知道,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


你可能感兴趣的:(原子访问--- Interlocked系列函数)