#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)
}
반응형
'운영체제 & 병렬처리 > Multithread' 카테고리의 다른 글
데드락 (1) 원인과 현상 (0) | 2022.09.06 |
---|---|
[5] #include <mutex> lock 처리로 동기화 (0) | 2022.09.06 |
[3] std::thread 와 인자 전달 (0) | 2022.09.06 |
[2] #include <thread> std::thread t(HelloThread); (0) | 2022.09.06 |
[1] 물리적인 쓰레드와 논리적인 쓰레드의 이해 (0) | 2022.09.05 |