본문 바로가기

Language/C C++

[C/C++]std::mutex

반응형

[C/C++]std::mutex

멀티 쓰레드를 이용한 프로그램을 작성할 때 공유 자원의 관리에 주의해야한다. 여러 쓰레드가 동시에 같은 데이터를 변경할 경우 의도하지 않은 결과를 초래할 수 있다. 이런 멀티 쓰레드 환경에서 발생하는 문제점을 동기화 메커니즘으로 해결할 수 있다.

먼저 동기화를 사용하지 않을 경우 발생하는 문제를 살펴보자.

#include <thread>    //std::thread
#include <cstdio>    //printf()

using namespace std;

int sum = 0;
void Add(int cnt)
{
    for (int i = 0; i < cnt; i++)
    {
        sum += 1;
    }
}

void Sub(int cnt)
{
    for (int i = 0; i < cnt; i++)
    {
        sum -= 1;
    }
}

int main()
{
    int cnt = 1000000;
    thread t1(Add, cnt);
    thread t2(Sub, cnt);

    t1.join();
    t2.join();

    printf("sum : %d\n", sum);
    return 0;
}

Add/Sub함수에서 for문을 순회하며 전역 변수 sum을 1씩 더하거나 뺀다. 이 함수들을 각각 쓰레드로 실행시켜 같은 횟수만큼 순회하도록 했다. 더하고 빼는 횟수가 같으므로 sum이 0이 되어야하지만 실행할 때마다 다른 값이 나오게 된다. 서로 다른 쓰레드에서 공통 변수 sum에 접근하여 값을 변경시키기 때문이다.

그림

따라서 공통 변수에 접근하는 부분을 제한하여 한번에 하나의 쓰레드만 들어올 수 있게 해줘야한다. C++에서 제공하는 std::mutex클래스를 사용한 코드이다.

#include <mutex>
int sum = 0;
mutex mtx;
void Add(int cnt)
{
    for (int i = 0; i < cnt; i++)
    {
        mtx.lock();
        sum += 1;
        mtx.unlock();
    }
}

void Sub(int cnt)
{
    for (int i = 0; i < cnt; i++)
    {
        mtx.lock();
        sum -= 1;
        mtx.unlock();
    }
}

뮤텍스로 lock을 호출한 후, unlock을 호출할 때까지는 다른 스레드는 대기를 하고, 락을 풀면 대기하고 있는 스레드 중 하나가 락을 건 후 공유 자원을 사용한다. 따라서 락을 건 후에는 꼭 락을 풀어야 하고, 락을 사용이 너무 빈번하면 쓰레드의 대기시간이 길어지기 때문에 병렬 프로그래밍의 장점이 약해진다. 따라서 적절한 부분에서 사용해야한다.

bool try_lock();

mutex 객체에는 try_lock()이라는 멤버함수도 있다. lock이 잠금 시도 후 unlock이 호출될 때 까지 대기하는 반면 try_lock()은 즉시 결과를 반환한다. 이를통해 lock을 얻지 못했을 경우의 다른 처리를 할 수 있도록 로직을 분리할 수 있다.

참고로 std::mutex는 Windows OS에서는 내부적으로 크리티컬섹션을 사용한다고 한다.

반응형

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

[C++] 클래스 생성자  (0) 2020.04.09
[C++]inline 함수  (0) 2019.01.23
[C/C++] 소켓 프로그래밍  (0) 2019.01.04
[C/C++] const keyword  (0) 2019.01.04
[C++]optional  (0) 2018.12.06