程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++拾遺--多線程:原子操作解決線程沖突

C++拾遺--多線程:原子操作解決線程沖突

編輯:C++入門知識

C++拾遺--多線程:原子操作解決線程沖突


C++拾遺--多線程:原子操作解決線程沖突

前言

在多線程中操作全局變量一般都會引起線程沖突,為了解決線程沖突,引入原子操作。

正文

1.線程沖突

 

#include 
#include 
#include 
#include 
int g_count = 0;
void count(void *p)
{
	Sleep(100);    //do some work
	//每個線程把g_count加1共10次
	for (int i = 0; i < 10; i++)
	{
		g_count++;
	}
	Sleep(100);   //do some work
}
int main(void)
{
	printf(******多線程訪問全局變量演示***by David***
);
	//共創建10個線程
	HANDLE handles[10];
	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			handles[j] = _beginthread(count, 0, NULL);
		}
		WaitForMultipleObjects(10, handles, 1, INFINITE);
		printf(%d time g_count = %d
, i, g_count);
		//重置
		g_count = 0;
	}
	getchar();
	return 0;
}
運行

 

\

理論上,g_count的最後結果應該是100,可事實卻並非如此,不但結果不一定是100,而且每次的結果還很可能不一樣。原因是,多個線程對同一個全局變量進行訪問時,特別是在進行修改操作,會引起沖突。詳細解釋:

設置斷點,查看反匯編

\

g_count++;被分為三步操作:

1.把g_count的內容從內存中移動到寄存器eax

2.把寄存器eax加1

3.把寄存器eax中的內容移動到內存g_count的地址

三步以後,g_count的值被順利加1。

cpu執行的時間片內包含多條指令,每條指令執行期間不會被打斷,但如果一個操作包含多條指令,則很有可能該操作會被打斷。g_count++;就是這樣的操作。

g_count被順利加到100的前提:每次加1,都建立在上一次加1順利完成的基礎上。也就是說,如果上一次加1被打斷,這一次的加1就得不到上一次加1的累積效果。自然,最後的結果,多半會小於100。

 

2.原子操作

所謂原子操作,是指不會被線程調度機制打斷的操作,操作一旦開始,就得執行到結束為止。原子操作可以是一個步驟,也可以是多個操作步驟,但是其順序是不可以被打亂,或者切割掉只執行部分。原子操作一般靠底層匯編實現。

在頭文件winnt.h中提供了很多的原子操作函數,它們使用自加鎖的方式,保證操作的原子性,如自增操作

InterlockedIncrement,

函數原型

LONG CDECL_NON_WVMPURE InterlockedIncrement(
_Inout_ _Interlocked_operand_ LONG volatile *Addend
);

其它相關操作,請自行查看頭文件。

使用該函數,我們修改線程函數count。修改很簡單,只需把g_count++改為InterlockedIncrement((LONG)&g_count);即可。

運行如下

\

顯然,在原子操作下,我們肯定是可以得到正確結果的。

 

 


 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved