본문 바로가기

TCP-IP 소켓 프로그래밍

스레드의 동기화를 이해해보자

스레드의 임계영역


스레드 A, B 가있다.

A는 1을 100번 빼는 걸 반복하고, B는 1을 100번 더하는 걸 반복한다. 하지만 둘은 같은 데이터를 건들고 있다고 한다면, 어떤 상황이 일어날까?

차근차근 더하고 빼고 해서 0이 될 것 같지만 실상 확인해 보면 0이라는 숫자는 잘 나오지 않는다. 심지어 계산할 때마다 계산 값이 다르게 나온다. 왜 이럴까?

스레드는 num에 있는 값을 바로 바꾸지 않고, 먼저 참조한 후 계산한 값을 적용시킨다. 이렇다 보니 이러한 현상이 생긴다.

먼저 쓰레드 B가 100이라는 값을 참조한 후, 1을 더해 101을 대입했다. 하지만 동시에 값을 건드린 Thread A가 100이라는 값에서 1을 뺀 99라는 값을 대입해 버렸기 때문에 이러한 현상이 발생한다.

이렇게 둘 이상의 쓰레드가 동시에 실행하면 문제를 일으키는 곳을 임계영역이라고 한다.

스레드 동기화


이러한 스레드의 문제점을 해결해주는 것을 쓰레드 동기화(Synchronization)이라고 한다. 쓰레드 동기화는 두 가지 측면에서 활용할 수 있다.

  1. 메모리 영역의 동시접근이 발생할 때
  2. 메모리 영역 접근 쓰레드의 실행 순서를 지정해야 할 때

뮤텍스

뮤텍스 Mutual Exclusion의 줄임말로 동시접근을 허용하지 않는다는 말로, 쓰레드 동기화 방법으로 주로 사용된다.

뮤텍스는 화장실로 비유해 설명하기도 한다.

 

Thread A: 똑똑! 누구 있나요?
Thread B: 볼일 보고 있어요.....

Thread A: 똑똑.
Thread B: 이제 다 끝났어요. 나갑니다.

사용 과정은 다음과 같다.

 

  • 들어갈 때 문을 잠그고 나올 때 연다
  • 사용 중이라면, 밖에서 대기한다.
  • 대기 중인 스레드는 여러 개가 될 수 있고, 순서대로 줄 서 있는다.
#include <mutex>

mutex value_mutex;
 
void increase_value()
{
    value_mutex.lock(); // 문 잠금
    value++;
    cout << value << endl;
    value_mutex.unlock(); // 문 잠금해제
}

마무리


스레드를 여러 개 사용할 때, 즉 멀티스레드 환경에선 임계영역, 실행 순서 등 신경 쓸 것이 많은 것 같다. 뮤텍스와 같은 동기화 방법을 남용하면 성능이 떨어지기도 하니 주의하자!

참고