03_Atomic

03_Atomic

날짜
생성자
ShalomShalom
카테고리
pararell
작성일
2023년 05월 03일
태그
이전 블로그
c++

예제 코드

#include <iostream> #include <thread> i32 sum = 0; void add() { for (i32 i = 0; i < 100'0000; i++) { sum++; } } void sub() { for (i32 i = 0; i < 100'0000; i++) { sum--; } } int main() { std::thread thread1(add); std::thread thread2(sub); thread1.join(); thread2.join(); cout << sum << endl; }

결과값

랜덤으로 나옴

이유

// 디버그 모드에서 디버그 -> 창 -> 디어셈블리 00007FF769642865 mov eax,dword ptr [sum (07FF76964F440h)] 00007FF76964286B inc eax 00007FF76964286D mov dword ptr [sum (07FF76964F440h)],eax // add 어셈블리 분석 int eax = sum; // add_1 eax = eax + 1; // add_2 sum = eax; // add_3 // Sub 어셈블리 분석 int eax = sum; //sub_1 eax = eax - 1; //sub_2 sum = eax; // sub_3
위 코드는 add와 서브를 어셈블리로 표현해본 그림이다.

설명을 하자면

  1. sub_1과 add_1에선 값 복사가 일어난다.
  1. 두개의 스레드는 모두 sum은 0이다.
  1. 각각 2번째 줄에 들어가게 되면 각각 -1, 또는 +1 된다.
  1. 즉, add와 sub가 서로 값을 인지한 결과값인 sum이 아니라 복사된 값에 --, ++를 하므로 sum = 1sum = -1;이 각각 호출되게 된다. (값을 덮어쓰게됨)
위와 같은 문제를 해결하기 위해선 여러 동기화 기법이 필요하다.

Atomic

아토믹이란 원자 즉, 더 이상 쪼개지지 않는 상태를 말함
  • 다 실행이 되거나
  • 다 실행이 안되거나
예를들어 게임에서 아이템 거래를 하면 다음과 같은 상황이다.
  1. A는 B에게 100원을 지불한다.
  1. B는 A에게 아이템을 준다.
위 상황이 순차적으로 일어난다 했을 때 1번만 실행이 되고 서버가 터졌다면 B에게는 아주 고마운 상황이 아닐 수 없다.
따라서 두가지 상황이 모두 동시에 일어날 꺼면 일어나고, 불가능하면 아예 안하는 것이 낫다.
즉, sum++과 sum--가 두 개의 스레드에서 동시에 발생할 수 밖에 없도록 하는 것이 atomic이라고 볼수 있다.

개선된 코드

#include <atomic> atomic<i32> sum = 0; void add() { for (i32 i = 0; i < 100'0000; i++) { //sum++; sum.fetch_add(1); // 해당 열이 시작 될때까지 } } void sub() { for (i32 i = 0; i < 100'0000; i++) { sum.fetch_add(-1); // 해당 열은 시작하지 않는다 } } int main() { std::thread thread1(add); std::thread thread2(sub); thread1.join(); thread2.join(); cout << sum << endl; }
  • fetch_add은 cpu 인스트럭션(명령어 집합)을 호출한다.
  • 따라서 위에서 본 assambly처럼 3단계로 진행하지 않고 하나의 단계로 +1 또는 -1이 진행된다.

댓글

guest