mutex
mutex(뮤텍스)란 다중 스레드 환경에서 공유 데이터에 대한 동시 접근을 제어하기 위해 사용하는 동기화 기법 중 하나이다. mutual exclusion의 약자로 여러 스레드가 동시에 접근하지 못하도록 lock 하는 기능을 제공한다.
mutex 사용 예시
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
mutex myMutex;
int sharedVariable = 0;
void incrementSharedVariableWithMutex(int iterations) {
for (int i = 0; i < iterations; ++i) {
myMutex.lock(); // 뮤텍스를 잠근다.
// 공유 데이터에 대한 안전한 업데이트를 수행한다.
++sharedVariable;
myMutex.unlock(); // 작업 완료 후 뮤텍스를 해제한다.
}
}
int main() {
const int numIterations = 1000000;
thread thread1(incrementSharedVariableWithMutex, numIterations);
thread thread2(incrementSharedVariableWithMutex, numIterations);
thread1.join();
thread2.join();
cout << "Final sharedVariable value: " << sharedVariable << endl;
return 0;
}
lock_guard
lock_guard는 C++ 표준 라이브러리에서 제공하는 클래스중 하나로 뮤텍스 관리에 대한 실수를 줄일 수 있도록 해준다. lock_guard를 사용하면 뮤텍스를 자동으로 잠그고 해당 범위(scope)가 벗어하면 lock_guard의 소멸자가 호출되어 자동으로 뮤텍스를 해제한다.
lock_guard 사용법
#include <iostream>
#include <mutex>
using namespace std;
mutex myMutex;
void someFunction() {
lock_guard<mutex> lock(myMutex);
// 뮤텍스가 잠겨 있을 때 이 코드 블록 안의 작업을 수행한다.
// 뮤텍스가 벗어나면 자동으로 뮤텍스가 해제된다.
// 다른 스레드는 이 뮤텍스를 획득하려고 시도하면 블록된다.
}
int main() {
// 여러 스레드에서 someFunction을 동시에 호출해도 뮤텍스 덕분에
// 한 번에 하나의 스레드만 someFunction 내의 코드를 실행할 수 있다.
return 0;
}
lock_guard 사용 예시
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
mutex myMutex;
int sharedVariable = 0;
void incrementSharedVariable(int iterations) {
lock_guard<mutex> lock(myMutex);
for (int i = 0; i < iterations; ++i) {
++sharedVariable;
}
}
int main() {
const int numIterations = 1000000;
thread thread1(incrementSharedVariable, numIterations);
thread thread2(incrementSharedVariable, numIterations);
thread1.join();
thread2.join();
cout << "Final sharedVariable value: " << sharedVariable << endl;
return 0;
}
뮤텍스는 주로 위 예시와 같이 공유 데이터의 안전한 업데이트를 보장하기 위해 사용된다.
스레드의 numIterations이 1000000이고 두 스레드에서 sharedVariable을 증가하고 있으니 최종 값이 2000000이 나와야 옳지만 뮤텍스를 사용하지 않으면 두 스레드가 동시에 sharedVariable을 업데이트하면서 잘못된 결과가 나올 수 있다.
처음에는 스레드 안에서 cout을 사용해 출력이 엉키는 경우가 있는데 테스트 해보려고 했는데 cout 스트림 자체는 내부적으로 스레드간 동기화가 되어 있어서 동시에 여러 스레드가 cout을 사용해도 출력이 엉키지 않았다.
lock_guard<mutex>의 적용 범위
위의 코드에선 한 스레드가 sharedVariable을 1000000번 증가시키는 동안 다른 스레드는 myMutex 뮤텍스에 대한 잠금을 기다려야 한다. 만약 두 스레드가 각각 독립적으로 1000000번 증가시키는게 아니라 번갈아가며 증가시키도록 하면 어떨까?
void incrementSharedVariable(int iterations) {
for (int i = 0; i < iterations; ++i) {
lock_guard<mutex> lock(myMutex);
++sharedVariable;
}
}
lock_guard<mutex>는 블록 범위에 락을 적용하는데 만약 lock_guard<std::mutex> lock(myMutex);를 for문 안쪽에 배치한다면 매 반복마다 뮤텍스를 잠그고 해제하기 때문에 lock_guard의 생성자와 소멸자가 매번 호출하면서 오버헤드가 발생하여 성능이 저하될 수 있다. 때문에 작업의 성격과 공유 자원에 대한 접근 패턴을 고려해서 lock_guard를 적용할 스코프를 선택해야 한다.
'프로그래밍 > C++' 카테고리의 다른 글
C/C++ ] start 명령어 사용 새로운 프롬프트 창에서 시스템 명령 사용하기 (0) | 2023.12.08 |
---|---|
C/C++ ] 키보드 메시지 후킹을 사용한 간단 키로거 (0) | 2023.11.21 |
C++ ] 인라인 함수 (0) | 2023.10.07 |
C++ ] 연속된 메모리 공간에 저장되는 벡터의 메모리 재할당 빈도 (0) | 2023.10.06 |
C++ ] enum class 사용법 (0) | 2023.10.03 |