뮤텍스(Mutex)

뮤텍스는 상호 배제(Mutual Exclusion)의 약자로 락(Lock)이라고도 한다.

이는 여러 쓰레드를 실행하는 환경에서 자원에 대한 접근 제한을 위한 동기화 메커니즘이다.

상호 배제 동시성 제어 정책(Mutual Exclusion Concurrency Control Policy)를 시행하도록 설계되었으며 가능한 다양한 방법을 통해 여러 고유한 구현이 존재한다

 

쉽게 말하자면 멀티스레딩 한경에서 공유 자원에 대한 접근을 조율하기 위해 사용되는 동기화 기법이다.

 

특징

상호배제

  • 뮤텍스는 한 번에 하나의 스레드만이 공유 자원에 접근할 수 있도록 보장한다.
  • 이를 통해 데이터의 일관성을 유지하고 경쟁상태(Race Condition)을 방지할 수 있다.

잠금과 해제

  • 잠금(Lock): 스레드가 공유 자원에 접근하기 전에 뮤텍스를 잠근다. 다른 스레드들은 뮤텍스가 해제될 때까지 해당 자원에 접근할 수 없다.
  • 해제(Unlock): 작업을 마친 스레드는 뮤텍스를 해제하여 다른 스레드들이 자원에 접근할 수 있ㄷ록 한다.

상태

  • 잠긴 상태(locked): 자원이 현재 다른 스레드에 의해 사용 중임을 나타낸다.
  • 해제된 상태(Unlocked): 자원이 현재 사용 가능함을 나타낸다.

데드락

뮤텍스를 사용할 때 주의해야 할 점 하나이다.

두 개 이상의 스레드가 서로 뮤텍스를 잠근 상태에서 상대방의 뮤텍스가 해제되기를 기다릴 때 발생한다.

이를 방지하기 위해 뮤텍스를 사용하는 순서를 정하거나, 타임아웃을 설정하는 등의 방법을 사용할 수 있다.


코드

#include <QCoreApplication>
#include <QThread>
#include <QMutex>
#include <QMutexLocker>
#include <QDebug>

class Worker : public QThread {
public:
    Worker(QMutex *mutex, int *counter) : mutex(mutex), counter(counter) {}

protected:
    void run() override {
        for (int i = 0; i < 100; ++i) {
            QMutexLocker locker(mutex);
            (*counter)++;
        }
    }

private:
    QMutex *mutex;
    int *counter;
};

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);

    const int numThreads = 10;
    QMutex mutex;
    int counter = 0;

    Worker *workers[numThreads];

    for (int i = 0; i < numThreads; ++i) {
        workers[i] = new Worker(&mutex, &counter);
        workers[i]->start();
    }

    for (int i = 0; i < numThreads; ++i) {
        workers[i]->wait();
        delete workers[i];
    }

    qDebug() << "Final counter value:" << counter;

    return a.exec();
}

 

설명

protected:
    void run() override {
        for (int i = 0; i < 100; ++i) {
            QMutexLocker locker(mutex);
            (*counter)++;
        }
    }
  • QThread의 run 메서드를 오버라이드하여 스레드가 실해할 작업을 정의한다.
  • QMutexLocker locker(mutex): 뮤텍스를 잠그는 QMutexLocker 객체를 생성한다. 이 객체는 스코프를 벗어날 때 자동으로 뮤텍스를 해제한다.
  • (*counter)++: 뮤텍스로 보호된 공유 자원 counter를 증가시킨다.
    for (int i = 0; i < numThreads; ++i) {
        workers[i] = new Worker(&mutex, &counter);
        workers[i]->start();
    }
  • Worker 객체를 생성하고 초기화 및 스레드를 시작한다.
    for (int i = 0; i < numThreads; ++i) {
        workers[i]->wait();
        delete workers[i];
    }
  • 스레드가 종료될 때까지 기다린 후, 동적으로 생성된 Worker 객체를 삭제한다.