오보에블로그

[C++] 멀티 스레드 와 mutex 본문

C++ & C#/C++

[C++] 멀티 스레드 와 mutex

(OBO) 2022. 2. 27. 16:14
728x90

멀티 스레드란?

  • 멀티 스레드란, 하나의 프로세스 안의 여러개의 스레드를 병렬적으로 실행시키는 것을 의미한다.
  • 멀티 프로세스와 다르게 프로세스 안의 자원을 공유할 수 있어서, 컨텍스트 스위치 비용이 적다는 장점이 있다.
  • 하지만, 여러 스레드에서 동시 자원을 사용할 때 문제가 발생할 수 있다. 아래의 코드 예시를 확인해보자.
#include <thread>
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int counter = 0;

void func()
{
    for (int i = 0; i < 100000; i++)
        counter += 1;
}

int main() {

    vector<thread> ts;

    // counter에 1씩 100000번 더하는 스레드 5개 생성
    for (int i = 0; i < 5; i++)
        ts.push_back(thread(func));

    for (int i = 0; i < 5; i++) {
        ts[i].join();
    }
    cout << counter;
    return 0;
}
  • 아래의 출력물은 counter500000 이 나오는 게 이상적이지만, 그것보다 작은 수가 출력될 것이다.
  • 그 이유는 멀티 스레드로 인해 공유 자원으로 사용하는 counter의 연산과정이 제대로 이루어지지 않고 있기 때문이다.
  • counter += 1 부분에 중단점을 걸어서 실행시켰을 때 디스어셈블리 코드를 펼쳐보면 아래와 같다.

  • 이를 풀어서 보면 다음과 같다. eax 는 64비트 레지스터 rax의 절반크기를 의미, 하지만 여기서 중요하지는 않다.
mov eax, dword ptr [counter] ; 1. counter의 값을 eax에 복사한다.
inc eax                      ; 2. eax을 1 증가시킨다.
mov dword ptr [counter], eax ; 3. eax을  counter 값에 복사한다.  
  • 코드 상으로는 한줄이지만, 컴퓨터는 세가지의 연산 과정을 거쳐서 counter += 1 를 수행하고 있었던 것이었다..! 만약에 그렇다면,, 현재 counter0 인 상태에서 2개의 스레드가 해당 어셈블리 과정을 진행한다고 할 때
  • A , B 스레드 모두 1 번 과정을 진행하고 2로 넘어가는 중이라고하자.
  • 1번 과정 진행 시 A , B 스레드 상에서 모두 eax0 이 할당 되고, 각각 1씩 증가 시키고, 결과적으로 counter 에는 1이 복사될 것이다.

  • 우리는 이를 해결하기 위해서 counter 와 같은 자원에 한번에 하나의 스레드만 접근할 수 있도록 하고 싶다.
  • C++ 에서는 이를 위해서 mutex라는 객체를 사용할 수 있다.

mutex

  • mutex 레퍼런스
  • void lock() : 해당 mutex를 잠근다. 이미 다른 스레드에서 잠그었다면 잠글 수 있을 때까지 다음 코드를 실행시키지 않는다.]
  • bool try_lock() : 해당 mutex를 잠근다. 성공했다면 true, 이미 다른 스레드에서 잠그었다면 false 리턴
  • void unlock() : 해당 mutex 잠금을 해제한다.
  • 아래와 같이 이전 코드에 mutex을 추가해서 올바른 출력 결과를 만들 수 있다.
#include <thread>
#include <iostream>
#include <vector>
#include <algorithm>
#include <mutex>

using namespace std;


mutex mtx; // mutex 객체 생성
int counter = 0;

void func()
{
    mtx.lock(); // 잠금
    for (int i = 0; i < 100000; i++)
        counter += 1;
    mtx.unlock(); // 해제
}

int main() {

    vector<thread> ts;

    for (int i = 0; i < 5; i++)
        ts.push_back(thread(func));

    for (int i = 0; i < 5; i++) {
        ts[i].join();
    }
    cout << counter;
    return 0;
}
728x90

'C++ & C# > C++' 카테고리의 다른 글

dll 생성 예제  (0) 2022.11.06
[C++] Modern C++ 버전 별 문법 정리  (0) 2022.05.01
[C++] std::this_thread 사용법  (0) 2022.02.24
[C++] std::thread 사용법  (0) 2022.02.24
[C++Docs] 타입 최대 최소 define 상수 라이브러리  (0) 2022.01.10