C++의 동시성
C++11부터 thread 클래스를 공식적으로 지원하여 멀티 스레드 프로그램을 만들 수 있게 되었다.
싱글 스레드로 작업하기
스레드를 만드려면 std::thread 클래스의 인스턴스를 만들고, 인자로 함수를 전달하면 된다.
이후 std::join()을 호출해서 스레드가 작업을 완료할 때 까지 메인 스레드를 대기시키면 된다.
void threadProc()
{
std::cout << "thread ID: ";
std::cout << std::this_thread::get_id() << '\n';
}
std::thread thread1(threadProc);
thread1.join();
스레드의 흐름은 정확히 예측할 수 없고, 스레드간의 실행 순서가 달라지거나 겹칠 수 있다.
멀티 스레드로 작업하기
두 개 이상의 스레드(메인 스레드 제외)를 동시에 실행한다.
std::thread threads[5];
for (int i = 0; i < 5; ++i) threads[i] = std::thread(threadProc);
for (auto& thread : thread) thread.join();
인자로 람다 표현식을 넘겨주는것도 당연히 가능하다.
뮤텍스를 이용한 스레드 동기화
동기화 문제 해결하기
스레드 내에서 공유 자원을 사용하면 동기화 문제가 발생할 수 있다.
하나의 자원을 여러 스레드에서 접근하여 값을 변경시킬 때, 동시에 접근하면 의도하지 않은 결과가 나올 수 있다.
뮤텍스를 이용하면 공유 자원에 접근하는 스레드를 하나로 제한시킬 수 있기 때문에 동기화 문제가 해결될 수 있다.
int counter = 0;
std::mutex mtx;
std::thread threads[5];
for (int i = 0; i < 5; ++i) {
threads[i] = std::thread([&counter, &mtx]() {
mtx.lock();
++counter;
mtx.unlock();
});
}
for (auto& thread : threads) thread.join();
주의할 점으로는 뮤텍스를 lock으로 잠궜다면 사용한 이후 반드시 unlock으로 풀어주어야 한다. 그렇지 않으면 뮤텍스가 반환되지 않아서 다른 스레드들은 뮤텍스를 영원히 기다리게 된다.
자동으로 잠금 해제하기
만약 뮤텍스의 unlock을 잊었거나 예외 발생 등으로 잠금이 해제되지 못하고 유효범위를 빠져나온다면 문제가 발생한다.
이 문제는 뮤텍스의 잠금과 해제를 자동으로 해주는 자원 관리 클래스 std::lock_guard<mutex>를 사용하면 된다.
...
threads[i] = thread([&counter, &mtx]) {
for (int i = 0; i < 10000; ++i)
{ // lock_guard의 유효 범위
std::lock_guard<std::mutex> guard(mtx);
++counter;
} // lock_guard의 유효 범위
}
선언 및 초기화 될 때, 생성자에서 뮤텍스를 잠그고 소멸자에서 잠금을 해제하기 때문에 실수로 잠금 해제를 하지 않거나 예외가 발생해도 유효범위를 빠져나갈 때 소멸자가 호출되어 자동으로 잠금이 해제된다.
recursive_mutex로 데드락 방지하기
하나의 뮤텍스로 여러 개의 lock_guard를 사용하면 데드락이 발생한다.
struct Math {
public:
void multiplexer(int i) { std::lock_guard<mutex> lock(mtx); ... }
void divisor(int i) { std::lock_guard<mutex> lock(mtx); ... }
void runAll(int a) {
std::lock_guard<std::mutex> lock(mtx);
multiplexer(a); // 안에서 lock_guard가 또 사용된다
divisor(a);
}
private:
std::mutex mtx;
};
두 곳의 함수에서 동일한 뮤텍스를 사용하여 lock_guard 객체를 생성하기 때문에 데드락에 빠진다.
이 때, lock_guard의 템플릿 인자로 mutex 대신 recursive_mutex를 사용하면 동일한 뮤텍스로 한 번 이상의 잠금 처리를 하면서 데드락도 방지할 수 있다.
'도서 > 모던 C++로 배우는 함수형 프로그래밍' 카테고리의 다른 글
메타프로그래밍으로 코드 최적화 (2) (0) | 2022.12.11 |
---|---|
메타프로그래밍으로 코드 최적화 (1) (0) | 2022.12.10 |
지연 평가로 실행 늦추기 (1) (0) | 2022.12.10 |
재귀 함수 호출 (2) (0) | 2022.12.09 |
재귀 함수 호출 (1) (0) | 2022.12.09 |