멀티스레딩, 동시성 및 뮤텍스 개요

2024-07-27

멀티스레딩, 동시성 및 뮤텍스 개요

동시성은 여러 개의 작업이 시간적으로 겹쳐 실행되는 것을 의미합니다. 멀티스레딩은 동시성을 구현하는 한 가지 방법이지만, 다른 방법도 있습니다. 예를 들어, 협동 프로세스를 사용하여 여러 프로세스가 서로 상호 작용할 수도 있습니다.

뮤텍스상호 배제를 구현하는 데 사용되는 동기화 도구입니다. 상호 배제는 한 번에 하나의 스레드만 특정 코드 섹션(임계 구역이라고 함)에 액세스할 수 있도록 하는 것을 의미합니다. 이는 데이터 손상을 방지하는 데 중요합니다. 예를 들어, 여러 스레드가 동시에 은행 계좌의 잔액을 업데이트하려고 하면 잔액이 잘못될 수 있습니다. 뮤텍스를 사용하면 한 번에 하나의 스레드만 계좌 잔액을 업데이트할 수 있으므로 데이터 무결성을 유지할 수 있습니다.

뮤텍스 작동 방식

뮤텍스는 일반적으로 잠금이라는 개념을 사용하여 구현됩니다. 스레드가 임계 구역에 들어가려면 뮤텍스 잠금을 얻어야 합니다. 잠금이 사용 중이면 스레드는 잠금이 해제될 때까지 기다려야 합니다. 스레드가 임계 구역을 벗어나면 뮤텍스 잠금을 해제해야 합니다.

뮤텍스는 하드웨어 또는 소프트웨어로 구현될 수 있습니다. 하드웨어 뮤텍스는 일반적으로 CPU에서 제공되는 특수 명령을 사용하여 구현됩니다. 소프트웨어 뮤텍스는 일반적으로 세마포어 또는 다른 동기화 도구를 사용하여 구현됩니다.

뮤텍스 사용 사례

뮤텍스는 다음과 같은 다양한 상황에서 사용됩니다.

  • 공유 자원에 대한 액세스 제어: 여러 스레드가 동시에 동일한 공유 자원에 액세스하려고 하면 뮤텍스를 사용하여 데이터 손상을 방지할 수 있습니다.
  • 임계 구역 보호: 임계 구역은 데이터를 업데이트하거나 다른 중요한 작업을 수행하는 코드 섹션입니다. 뮤텍스를 사용하면 한 번에 하나의 스레드만 임계 구역에 액세스할 수 있으므로 데이터 무결성을 유지할 수 있습니다.
  • 조건 동기화: 뮤텍스를 사용하여 특정 조건이 충족될 때까지 스레드가 기다리도록 할 수 있습니다. 예를 들어, 한 스레드가 프린터를 사용하고 있는 경우 다른 스레드는 뮤텍스를 사용하여 프린터가 사용 가능해질 때까지 기다릴 수 있습니다.

뮤텍스의 장점 및 단점

장점:

  • 데이터 손상을 방지하는 데 도움이 됩니다.
  • 코드 동기화를 간편하게 합니다.
  • 리소스 사용 효율성을 향상시킬 수 있습니다.

단점:

  • 데드락 발생 가능성이 있습니다.
  • 오버헤드가 발생할 수 있습니다.
  • 코드 복잡성을 증가시킬 수 있습니다.

결론




뮤텍스 예제 코드

#include <iostream>
#include <thread>
#include <mutex>

using namespace std;

int counter = 0;
mutex mtx;

void incrementCounter() {
  for (int i = 0; i < 1000; i++) {
    mtx.lock(); // 임계 구역에 들어가기 전에 뮤텍스 잠금
    counter++;
    mtx.unlock(); // 임계 구역을 벗어난 후 뮤텍스 잠금 해제
  }
}

int main() {
  thread t1(incrementCounter);
  thread t2(incrementCounter);

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

  cout << "counter: " << counter << endl;

  return 0;
}

이 코드에서 incrementCounter 함수는 뮤텍스를 사용하여 counter 변수를 안전하게 증가시킵니다. 먼저 mtx.lock() 함수를 호출하여 뮤텍스 잠금을 얻습니다. 잠금을 얻으면 스레드가 임계 구역에 들어갈 수 있습니다. 임계 구역에서 counter 변수를 1 증가시킵니다. 마지막으로 mtx.unlock() 함수를 호출하여 뮤텍스 잠금을 해제합니다.

두 스레드가 모두 incrementCounter 함수를 동시에 실행하면 데이터 손상이 발생할 수 있습니다. 예를 들어 한 스레드가 counter 변수를 읽고 1을 더하기 전에 다른 스레드가 counter 변수를 읽고 1을 더할 수 있습니다. 이 경우 counter 변수의 최종 값은 2가 아닌 1이 됩니다.

뮤텍스를 사용하면 이러한 데이터 손상을 방지할 수 있습니다. 한 번에 하나의 스레드만 임계 구역에 있을 수 있으므로 counter 변수는 항상 정확하게 증가됩니다.

추가 예제

다음은 뮤텍스를 사용하여 조건 동기화를 구현하는 방법을 보여주는 예제입니다. 이 예제에서는 한 스레드가 프린터를 사용하고 있을 때 다른 스레드가 프린터가 사용 가능해질 때까지 기다리는 방법을 보여줍니다.

#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

using namespace std;

mutex mtx;
condition_variable cv;
bool printerInUse = false;

void printDocument() {
  mtx.lock();

  while (printerInUse) {
    cv.wait(mtx); // 프린터가 사용 가능해질 때까지 기다림
  }

  printerInUse = true;

  cout << "printing document..." << endl;

  sleep(2); // 인쇄 작업 시뮬레이션

  printerInUse = false;
  cv.notify_one(); // 다른 스레드가 프린터를 사용할 수 있도록 알림

  mtx.unlock();
}

int main() {
  thread t1(printDocument);
  thread t2(printDocument);

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

  return 0;
}

이 코드에서 printDocument 함수는 프린터를 사용하기 전에 뮤텍스를 잠금합니다. 프린터가 사용 중이면 cv.wait(mtx) 함수를 호출하여 프린터가 사용 가능해질 때까지 기다립니다. 프린터가 사용 가능하면 printerInUse 플래그를 true로 설정하고 문서를 인쇄합니다. 마지막으로 printerInUse 플래그를 false로 설정하고 cv.notify_one() 함수를 호출하여 다른 스레드가 프린터를 사용할 수 있도록 알립니다.

두 스레드가 모두 printDocument 함수를 동시에 실행하면 한 스레드만 프린터를 한 번에 사용할 수 있습니다. 다른 스레드는 프린터가 사용 가능해질 때까지 기다립니다.




뮤텍스 대체 방법

  • 데드락 발생 가능성: 뮤텍스를 잘못 사용하면 여러 스레드가 서로를 기다리는 상태로 막히는 데드락이 발생할 수 있습니다.
  • 오버헤드: 뮤텍스는 잠금 및 해제 작업에 관련된 오버헤드가 발생합니다. 이는 성능이 중요한 시스템에서는 문제가 될 수 있습니다.
  • 코드 복잡성 증가: 뮤텍스를 사용하면 코드가 더 복잡해질 수 있습니다.

다음은 뮤텍스 대신 사용할 수 있는 몇 가지 방법입니다.

  • 세마포어: 세마포어는 뮤텍스와 유사하지만, 한 번에 여러 스레드가 특정 리소스에 액세스할 수 있도록 허용합니다. 이는 프린터와 같은 자주 공유되는 리소스에 더 적합합니다.
  • 리디스 스핀락: 리디스 스핀락은 뮤텍스보다 빠르지만, 데드락 발생 가능성이 더 높습니다. 일반적으로 성능이 중요한 시스템의 저수준 코드에서만 사용됩니다.
  • CAS(Compare-And-Swap): CAS는 한 번에 하나의 스레드만 메모리 위치를 업데이트할 수 있도록 하는 원자 작업입니다. 뮤텍스 대신 사용될 수 있지만, CAS를 사용하는 코드는 더 복잡할 수 있습니다.
  • 티켓 락: 티켓 락은 데드락을 방지하는 데 도움이 되는 뮤텍스 유형입니다. 그러나 티켓 락은 리디스 스핀락보다 느리고 CAS보다 복잡합니다.

어떤 방법을 사용할지는 특정 상황에 따라 다릅니다. 뮤텍스는 간단하고 사용하기 쉬우며 대부분의 상황에 적합합니다. 그러나 데드락, 오버헤드 또는 코드 복잡성이 문제가 되는 경우 다른 방법을 고려해야 할 수도 있습니다.

추가 고려 사항

뮤텍스를 선택할 때 다음 사항도 고려해야 합니다.

  • 필요한 동기화 수준: 한 번에 하나의 스레드만 특정 코드 섹션에 액세스할 수 있도록 해야 하는 경우 뮤텍스가 적합합니다. 여러 스레드가 동시에 코드 섹션에 액세스할 수 있도록 허용해야 하는 경우 세마포어가 더 적합할 수 있습니다.
  • 성능: 성능이 중요한 경우 리디스 스핀락이나 CAS와 같은 더 빠른 동기화 방법을 고려할 수 있습니다. 그러나 이러한 방법은 뮤텍스보다 복잡하고 데드락 발생 가능성이 더 높을 수 있습니다.
  • 코드 복잡성: 뮤텍스는 간단하고 사용하기 쉬운 경향이 있습니다. 그러나 데드락을 방지하거나 특정 동기화 요구 사항을 충족하기 위해서는 추가 코드가 필요할 수 있습니다.

결론


multithreading concurrency mutex

multithreading concurrency mutex

경쟁 조건이란 무엇인가?

경쟁 조건은 일반적으로 다음과 같은 상황에서 발생합니다.공유 자원에 대한 비동기 액세스: 여러 스레드 또는 프로세스가 동시에 공유 변수나 데이터 구조에 액세스할 수 있는 경우 경쟁 조건이 발생할 가능성이 높습니다. 예를 들어


경쟁 조건이란 무엇인가?

경쟁 조건은 일반적으로 다음과 같은 상황에서 발생합니다.공유 자원에 대한 비동기 액세스: 여러 스레드 또는 프로세스가 동시에 공유 변수나 데이터 구조에 액세스할 수 있는 경우 경쟁 조건이 발생할 가능성이 높습니다. 예를 들어