++i在线程中为什么需要同步

zarelaky 2011-10-28 11:22:08
int i=0;
有如下两个线程函数
thread1(){
++i;
}
thread2() {
++i;
}

thread1和thread2中的++i为什么需要同步?
...全文
309 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
pgmsoul 2011-11-18
  • 打赏
  • 举报
回复
++i不是一条cPU指令,就这么简单,即使在单CPU上也不行,必须同步。

这是Windows的线程机制决定的,它可能在任何一条CPU指令切换线程(除非那些专门的函数)。
IONPhantom 2011-11-15
  • 打赏
  • 举报
回复
切换线程是操作系统的事情,在单CPU的情况下CPU根本不知道它当前为哪个线程工作,论询和切换线程是由操作系统来控制的
zarelaky 2011-11-13
  • 打赏
  • 举报
回复
CPU是借用时钟或外部中断切换线程的么? 不然一个执行流怎么实现线程切换?
horris 2011-11-09
  • 打赏
  • 举报
回复
递增、递减语句,如果变量是放在CPU寄存器的话,那么它们是原子操作,在单CPU上实际上不需同步保护。但是设想一下多CPU或多核CPU的情况,同步保护绝对必要。
IONPhantom 2011-11-07
  • 打赏
  • 举报
回复
++ 操作不是一个原子操作
督门提码 2011-11-02
  • 打赏
  • 举报
回复
++i分别在线程pthread1和pthread2中,pthread1和pthread2是资源共享的所有他们内部的++i操作要同步才不会导致处理出错,++i在pthread1和pthread2中分别抢占尽可能多的资源。
only_delusion 2011-11-01
  • 打赏
  • 举报
回复
对同一个地址进行了写入操作 当然需要同步了.
lijianli9 2011-10-31
  • 打赏
  • 举报
回复
只要保证你的操作是原子的,但是i++,++i编译后就变成了三条汇编指令,随意在任何时刻这两个线程在执行当中的三条指令的时候被打断,就会产生错误。
Lactoferrin 2011-10-30
  • 打赏
  • 举报
回复
inc [i]
xiaoshang_program 2011-10-30
  • 打赏
  • 举报
回复
return ::InterlockedIncrement( &_refNum );
return ::InterlockedDecrement( &_refNum );

用两个把
csx007700 2011-10-30
  • 打赏
  • 举报
回复
++i 和 i++都不是原子操作

翻译成汇编相当于

mov eax,[i]
inc eax
mov [i],eax


实际执行了三条汇编指令,所以要同步
gameslq 2011-10-29
  • 打赏
  • 举报
回复
为保修改的正确性、一致性 需要同步
需要说明的是
1。 ++i 相等于 i= i+ 1;
2。操作系统进行时间片切换是以线程为单位,也就是说一个线程a正在执行时,可能被打断,使线程a挂起,线程b开始执行那么多个线程针对一个资源进行修改就会出现问题。
假设
int i = 0;时
thread1 开始执行,当 cpu 刚计算完i+1 还没有把的i+1的结果(1)回写 到 i 变量时,此时thread1中断执行,开始执行 thread2
thread2 开始执行,thread2并顺利执行完,此时 i=1,并再次执行thread1
thread1 接着执行,并把i+1的结果(1)写回 i,即i=1
看到了吗,两个线程写入i的值都是1,并写了两遍 thread1覆盖掉了thread2更新的值
如果假设
thread1 里面 是 ++i;
thread2 里面 是 --1;
如果再上边的顺序执行,就会出现 thread1 覆盖掉thread2更新的结果值。
这个效果会比较明显。

kgzhw 2011-10-29
  • 打赏
  • 举报
回复
应该是需要的,不行就做个实验打印出来看看就知道了,这是我打印的结果
thread1 m_ndata = 87
thread2 m_ndata = 88
thread1 m_ndata = 90
thread2 m_ndata = 89
Lactoferrin 2011-10-29
  • 打赏
  • 举报
回复
这个可以做成原子操作
aCracker 2011-10-29
  • 打赏
  • 举报
回复
 #include "stdafx.h"  DWORD JGetHeroLevel = (DWORD)0x3c7950; DWORD JGetPlayerName = (DWORD)0x3c19e0; DWORD JGetPlayerNameAdd1 = (DWORD)0x3bdf50; DWORD JGetPlayerNameAdd2 = (DWORD)0x40bb60; DWORD JIsUnitVisible = (DWORD)0x3c8570; DWORD JIsUnitSelected = (DWORD)0x3c8880; DWORD JPlayer = (DWORD)0x3bc5b0; DWORD JGamePoint = (DWORD)0xacd44c; DWORD HookUnitAddress = (DWORD)0x42b0ed; DWORD HookTlsSetAddress = (DWORD)0x6d9ff8;  HMODULE GameDllBase;  char HookUnitBytes[5] = {0xe9}; char HookTlsSetBytes[5] = {0xe9}; char HookSendBytes[5] = {0xe8};  bool InDotA = true; bool State = 0; bool hasgotplayer; int ProcessId, ThreadId, ThreadTlsV, ThreadTlsT;  int s_unit[500]; int s_player[20]; char s_playername[20][100]; int s_selecttime[500][20]; int s_visibletime[500][20]; HWND htosend, hself;  void SendString(char * buf) { 	int k; 	COPYDATASTRUCT Dta ; 	Dta.dwData = 0x33; 	Dta.cbData = strlen(buf) + 1; 	Dta.lpData = (LPVOID)buf; 	k = SendMessage(htosend, WM_COPYDATA, (WPARAM)hself,(LPARAM)&Dta); }  void ConvertUTF8ToANSI(char * strUTF8, char * strANSI) { 	int nLen = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, (LPCTSTR)strUTF8, -1, NULL, 0); 	wchar_t * wszANSI = new wchar_t[nLen + 1]; 	memset(wszANSI, 0, nLen * 2 + 2); 	nLen = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, (LPCTSTR)strUTF8, -1, wszANSI, nLen); 	nLen = WideCharToMultiByte(CP_ACP, 0, wszANSI, -1, NULL, 0, NULL, NULL); 	char * szANSI = new char [nLen + 1]; 	memset(szANSI, 0, nLen + 1); 	WideCharToMultiByte(CP_ACP, 0, wszANSI, -1, szANSI, nLen, NULL, NULL); 	memcpy(strANSI, szANSI, nLen + 1); 	delete [] wszANSI; 	delete [] szANSI; }  int Check22(int tunit, int tplayer) { 	int k = 0; 	char tmp[100]; 	__try 	{  		__asm 		{ 			pushad 			push tplayer 			push tunit 			mov esi, 0x6F95A193 			call dword ptr [JIsUnitVisible] 			pop ecx 			pop edx 			mov k, eax 			popad 		} 		 	} 	__except(EXCEPTION_EXECUTE_HANDLER) 	{ 		sprintf(tmp, "IsUnitVisible error : player : 0x%x unit : 0x%x", tplayer, tunit); 		OutputDebugString(tmp); 		ExitThread(0); 		return 0; 	} 	return k; }    int Check21(int tunit, int tplayer) { 	int k = 0; 	char tmp[100]; 	__try 	{ 		__asm 		{ 			pushad 			push tplayer 			push tunit 			mov esi, 0x6F95A193 			call dword ptr [JIsUnitSelected] 			pop ecx 			pop edx 			mov k, eax 			popad 		}  		 	} 	__except(EXCEPTION_EXECUTE_HANDLER) 	{ 		sprintf(tmp, "IsUnitSelected error : player : 0x%x unit : 0x%x", tplayer, tunit); 		OutputDebugString(tmp); 		ExitThread(0); 		return 0; 	} 	return k; }  int GetAllPlayer2(int k) { 	int j = 0; 	__try 	{ 		__asm 		{ 			pushad 			push k 			call dword ptr [JPlayer] 			pop ecx 			mov j, eax 			popad 		} 			 	} 	__except(EXCEPTION_EXECUTE_HANDLER) 	{ 		return -1; 	} 	if(j >= 0x100000) 		return j; 	return 0; }  void GetAllPlayer() { 	int i, j, k, OldTls; 	char tmp[100]; 	memset(s_player, 0, sizeof(s_player)); 	for(i = 0 ; i <= 11 ; i++) 	{ 		OldTls = (int)TlsGetValue(ThreadTlsT); 		TlsSetValue(ThreadTlsT, (LPVOID)ThreadTlsV); 		k = GetAllPlayer2(i); 		TlsSetValue(ThreadTlsT, (LPVOID)OldTls);  		if(k > 0) 		{ 			s_player[++s_player[0]] = k; 			//if(k <= 0x100000 || k >= 0x100020)s_player[s_player[0]] = 0; 			sprintf(tmp, "PlayerID %d", k); 			OutputDebugString(tmp); 		}  	} }  void GetPlayerName(int tplayer, char * dst, int maxLen) { 	char * res; 	__try 	{ 		__asm 		{ 			mov ecx, tplayer 			call dword ptr [JGetPlayerNameAdd1] 			test eax, eax 			jnz A 			jmp B  			A: 			push 1 			mov ecx, eax 			call dword ptr [JGetPlayerNameAdd2]  			B: 			mov res, eax 		} 	} 	__except(EXCEPTION_EXECUTE_HANDLER) 	{ 		res = 0; 	} 	if(!res) 	{ 		memset(dst, 0, maxLen); 		return; 	} 	memcpy(dst, res, strlen(res) + 1); }   void GetAllPlayerName() { 	int i; 	memset(s_playername, 0, sizeof(s_playername)); 	for(i = 1 ; i <= 12 ; i++) 	{ 		GetPlayerName(s_player[i], s_playername[i], 100); 		ConvertUTF8ToANSI(s_playername[i], s_playername[i]); 		OutputDebugString(s_playername[i]); 	} }  int GetUnit2(int k) { 	int a = 0; 	__try 	{ 		__asm 		{ 			pushad 			push k 			call dword ptr [JGetHeroLevel] 			mov a, eax 			pop eax 			popad 		} 	} 	__except(EXCEPTION_EXECUTE_HANDLER) 	{ 		OutputDebugString("GetHeroLevel error"); 		return -1; 	} 	if(a)return 1; 	return 0; }  void GetUnit(int k) { 	int i, a, OldTls; 	char tmp[100]; 	 	if(k < 0x100000 || k > 0x200000)return;  	OldTls = (int)TlsGetValue(ThreadTlsT); 	TlsSetValue(ThreadTlsT, (LPVOID)ThreadTlsV); 		 	if(GetUnit2(k) == 1) 	{ 		for(i = 1 ; i <= s_unit[0] ; i++) 			if(s_unit[i] == k) break; 		if(i > s_unit[0]) 		{ 			s_unit[++s_unit[0]] = k; 			sprintf(tmp, "得到英雄ID : %d", k); 			if(!hasgotplayer) 			{ 				GetAllPlayer(); 				GetAllPlayerName(); 				hasgotplayer = true; 			} 			SendString(tmp); 			OutputDebugString(tmp); 		} 	} 	TlsSetValue(ThreadTlsT, (LPVOID)OldTls); }   int WINAPI CheckThread(LPVOID xx) { 	int i, j, tunit, tplayer, k, k1, k2, nowtime, OldTls; 	char tmp[100]; 	int * x;  	x = (int *)JGamePoint;  		/*if(*x) 			OutputDebugString("InGame"); 		else 			OutputDebugString("OutOfGame");*/  	/*if(!State && *x) 	{ 		s_unit.empty(); 		GetAllPlayer(); 		State = true; 		return; 	}*/ 	if(!*x) 	{ 		State = false; 		return 0; 	} 	if(!State)return 0; 	 	TlsSetValue(ThreadTlsT, (LPVOID)ThreadTlsV); 	 	for(j = 1 ; j <= s_player[0]  ; j++) 	{ 		for (i = 1 ; i <= s_unit[0]; i++) 		{ 			k = 0; 			tunit = s_unit[i]; 			tplayer = s_player[j]; 			//OldTls = (int)TlsGetValue(ThreadTlsT); 			 			k1 = Check21(tunit, tplayer); 			k2 = Check22(tunit, tplayer); 			k = k1 && !k2; 			//TlsSetValue(ThreadTlsT, (LPVOID)OldTls); 			nowtime = GetTickCount(); 			if(k && nowtime - s_selecttime[i][j] > 1000  && nowtime - s_visibletime[i][j] > 1000) 			{ 				sprintf(tmp, "玩家编号:%d 玩家名:%s 选中不可见单位:%d 时间:%d", j, s_playername[j], tunit, nowtime); 				SendString(tmp); 				OutputDebugString(tmp); 			} 			if(k1) 				s_selecttime[i][j] = nowtime; 			if(k2) 				s_visibletime[i][j] = nowtime; 		} 	} 	return 0; }  void Check() { 	CreateThread(0, 0, (LPTHREAD_START_ROUTINE)CheckThread, (LPVOID)0, 0, 0); }  void GetThreadTls(int j, int k) { 	char tmp[100]; 	if(GetCurrentThreadId() == ThreadId && k) 	{ 		if(ThreadTlsV != k || ThreadTlsT != j) 		{ 			ThreadTlsV = k; 			ThreadTlsT = j; 			sprintf(tmp, "GetMainThreadTls : 0x%x Type: 0x%x", k, j); 			OutputDebugString(tmp); 		} 	} }  __declspec(naked) void HookUnit() { 	__asm 	{ 		pushad 		push edx 		call GetUnit 		pop edx 		popad 		pop esi 		retn 4 	} }   __declspec(naked) void HookSend() { 	__asm 	{ 		pushad 		call Check 		popad  		sub edx, 1 		and eax, edx 		ret 		 	} }  __declspec(naked) void HookTlsSet() { 	__asm 	{ 		pushad 		push edx 		push ecx 		call GetThreadTls 		pop eax 		pop eax 		popad  		ret 	} }  int WINAPI Watcher(LPVOID k) { 	int * x; 	while(1) 	{ 		Sleep(100); 		x = (int *)JGamePoint;   		if(!State && *x) 		{ 			memset(s_unit, 0, sizeof(s_unit)); 			memset(s_selecttime, 0, sizeof(s_selecttime)); 			memset(s_visibletime, 0, sizeof(s_visibletime)); //			GetAllPlayer(); 			State = true; 			hasgotplayer = false; 			SendString("New Game"); 			OutputDebugString("New Game"); 			continue; 		} 		if(!*x) 		{ 			State = false; 			continue; 		} 		Check(); 		 	} 	return 0; }  	  void DllLoad() { 	char tm[100];  	hself = FindWindow(NULL, "Warcraft III"); 	htosend = FindWindow(NULL, "MapHackClickDetector"); 	GameDllBase = GetModuleHandle("game.dll"); 	JGetHeroLevel += (DWORD)GameDllBase; 	JGetPlayerName += (DWORD)GameDllBase; 	JGetPlayerNameAdd1 += (DWORD)GameDllBase; 	JGetPlayerNameAdd2 += (DWORD)GameDllBase; 	JIsUnitVisible += (DWORD)GameDllBase; 	JIsUnitSelected += (DWORD)GameDllBase; 	JPlayer += (DWORD)GameDllBase; 	JGamePoint += (DWORD)GameDllBase; 	HookUnitAddress += (DWORD)GameDllBase; 	HookTlsSetAddress += (DWORD)GameDllBase;   	int tmp = (DWORD)HookUnit - (DWORD)HookUnitAddress - 5; 	memcpy(HookUnitBytes + 1, (void *)&tmp, 4); 	WriteProcessMemory((HANDLE)-1, (LPVOID)HookUnitAddress, (LPCVOID)HookUnitBytes, 5, 0);  	tmp = (DWORD)HookTlsSet - (DWORD)HookTlsSetAddress - 5; 	memcpy(HookTlsSetBytes + 1, (void *)&tmp, 4); 	WriteProcessMemory((HANDLE)-1, (LPVOID)HookTlsSetAddress, (LPCVOID)HookTlsSetBytes, 5, 0);  	ProcessId = GetCurrentProcessId(); 	THREADENTRY32 te32 = {sizeof(te32)}; 	HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,0); 	if( Thread32First( hThreadSnap, &te32) ) 	{ 		do 		{ 			if(ProcessId == te32.th32OwnerProcessID ) 			{ 				ThreadId = te32.th32ThreadID; 				break; 			} 		}while( Thread32Next( hThreadSnap, &te32) ); 	}  	CreateThread(0, 0, (LPTHREAD_START_ROUTINE)Watcher, (LPVOID)0, 0, 0); 	  	SendString("已启动"); 	sprintf(tm, "MainThreadId : %d", ThreadId); 	OutputDebugString(tm); 	OutputDebugString("DllLoad"); }   BOOL APIENTRY DllMain( HMODULE hModule,                        DWORD  ul_reason_for_call,                        LPVOID lpReserved 					 ) { 	switch (ul_reason_for_call) 	{ 	case DLL_PROCESS_ATTACH: 		DllLoad(); 	case DLL_THREAD_ATTACH: 	case DLL_THREAD_DETACH: 	case DLL_PROCESS_DETACH: 		break; 	} 	return TRUE; } 
Gloveing 2011-10-28
  • 打赏
  • 举报
回复
看你做什么了;
只是这样的话,无所谓;如果最终需要用到确定的 i值,就要同步
因为 thread1 thread2执行开始后时,你不能保证它到底在哪里执行,两个线程中都可以修改i
最终i的值不确定
hztj2005 2011-10-28
  • 打赏
  • 举报
回复
设i=55;
如果thread1执行++i,则为56,但还没有写回内存时,时间片到,被操作系统切换。

如果thread2又执行++i,又是56,写回内存,内存中i=56;

thread1再次获得时间片时,再写一次,i还是56;

而实际上应该是57.



15,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 进程/线程/DLL
社区管理员
  • 进程/线程/DLL社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧