반응형

 

#include "pch.h"
#include <iostream>
#include "CorePch.h"
#include <thread>	
#include <vector>

using namespace std;


int32 sum = 0;

void add()
{
	for (int32 i=0;i<100'0000;++i)
	{
		++sum;
	}
}

void sub()
{
	for (int32 i = 0; i < 100'0000; ++i)
	{
		--sum;
	}
}

int main()
{
	std::cout << sum << endl;

	vector<std::thread> t;
	t.push_back(thread(add));
	t.push_back(thread(sub));

	for (auto& th : t)
	{
		th.join();
	}
	std::cout << " after " << std::endl;
	std::cout << sum << endl;

	

	


	return 0;
}

 

동기화가 제대로 이루어 지지 않는 것을 볼 수 있다

 

 

 

++sum 부분의 어셈 블리를 보면

 

 

 

주석 과 같은 설명으로 처리 되는데

 

증가

00007FF6C65663F9  mov         dword ptr [rbp+4],eax  
00007FF6C65663FC  cmp         dword ptr [rbp+4],0F4240h  
00007FF6C6566403  jge         add+45h (07FF6C6566415h)  
	{
		++sum;
00007FF6C6566405  mov         eax,dword ptr [sum (07FF6C6575450h)]  //sum 변수를 eax 에
00007FF6C656640B  inc         eax     //eax 값 1 증가
00007FF6C656640D  mov         dword ptr [sum (07FF6C6575450h)],eax   //eax 값을  sum 변수에 대입
	}
00007FF6C6566413  jmp         add+24h (07FF6C65663F4h)

 -- 도 비슷하게 처리 된다

 

 

감소

00007FF6C656342C  cmp         dword ptr [rbp+4],0F4240h  
00007FF6C6563433  jge         sub+45h (07FF6C6563445h)  
	{
		--sum;
00007FF6C6563435  mov         eax,dword ptr [sum (07FF6C6575450h)]  
00007FF6C656343B  dec         eax  
00007FF6C656343D  mov         dword ptr [sum (07FF6C6575450h)],eax  
	}
00007FF6C6563443  jmp         sub+24h (07FF6C6563424h)  
}​

 

 

++ 나 -- 는 명령어 한줄이고 아니고 3줄씩 이기 때문에 타이밍상 ++ 실행 하던 도중에 sum 이 1기 되기 전에

sum 가 중간에 먼저 실행되어 sum 은 -1 이 되고 이때는 -1 이지만 다시 나머지 ++ 함수로 되돌아가 이전 레지스터에 있떤 eax 값 1 값이 sum 에 덮어 써지게 됨으로 sum 은 1이 된다,  sum 과 add 를 동시에 실행했음에도 불구하고 

 

즉 동기화의 문제가 발생하게 된다

그리고 이런 상태가 계속 누적 되면 결과는 이처럼 완전 잘못된 상태가 된다 

 

 

그래서 이걸 atomic 하게 만들어 문제를 해결 할수 있다 (더이상 쪼개 질 수 없는 단위)

Atomic : all or nothing 즉 다 실행되거나 모두 실행되지 않는 연산을 말한다

연산이 그렇게 빠르진 않음 

 

 

sum 을 atomic 으로 처리하면 동기화 문제를 해결 할수 있음

 

#include "pch.h"
#include <iostream>
#include "CorePch.h"
#include <thread>	
#include <atomic>			//멀티 플랫폼에서 작동 가능
#include <vector>

using namespace std;


//이렇게 사용하면 atomic 사용 끝
atomic<int32> sum = 0;

void add()
{
	for (int32 i=0;i<100'0000;++i)
	{
		++sum;
	}
}

void sub()
{
	for (int32 i = 0; i < 100'0000; ++i)
	{
		--sum;
	}
}

int main()
{
	std::cout << sum << endl;

	vector<std::thread> t;
	t.push_back(thread(add));
	t.push_back(thread(sub));

	for (auto& th : t)
	{
		th.join();
	}
	std::cout << " after " << std::endl;
	std::cout << sum << endl;

	

	


	return 0;
}

 

 

 

 

atomic.fetch_add 를 사용해도 결과는 같다

#include "pch.h"
#include <iostream>
#include "CorePch.h"
#include <thread>	
#include <atomic>			//멀티 플랫폼에서 작동 가능
#include <vector>

using namespace std;


//이렇게 사용하면 atomic 사용 끝
atomic<int32> sum = 0;

void add()
{
	for (int32 i=0;i<100'0000;++i)
	{
		//++sum;
		sum.fetch_add(1);
	}
}

void sub()
{
	for (int32 i = 0; i < 100'0000; ++i)
	{
		//--sum;
		sum.fetch_add(-1);
	}
}

int main()
{
	std::cout << sum << endl;

	vector<std::thread> t;
	t.push_back(thread(add));
	t.push_back(thread(sub));

	for (auto& th : t)
	{
		th.join();
	}
	std::cout << " after " << std::endl;
	std::cout << sum << endl;

	

	


	return 0;
}

 

 

어셈블리 내용을 보면 명령어가 한출로 처리 되는 것을 알수 있다 (내부적으로 다른 함수를 실행시켜서 완료 되면 빠져나옴)


00007FF6E24A29A3  jge         add+4Eh (07FF6E24A29BEh)  
	{
		//++sum;
		sum.fetch_add(1);
00007FF6E24A29A5  mov         r8d,5  
00007FF6E24A29AB  mov         edx,1  
00007FF6E24A29B0  lea         rcx,[sum (07FF6E24B5450h)]  

//한줄로 처리 됨
00007FF6E24A29B7  call        std::vector<std::thread,std::allocator<std::thread> >::capacity (07FF6E24A1857h)  


	}

 

반응형

+ Recent posts