본문: C++는 멀티스레딩을 지원하는 강력한 언어입니다. 그러나 여러 스레드가 동시에 접근하면 문제가 발생할 수 있습니다. 이러한 문제를 해결하기 위해 스레드 안전한 프로그래밍 패턴을 적용해야 합니다.
1. 뮤텍스와 락
C++에서의 스레드 안전한 프로그래밍을 위해 가장 많이 사용되는 패턴은 뮤텍스와 락(mutex and lock)입니다. 뮤텍스는 하나의 스레드만 접근할 수 있도록 하는 동기화 메커니즘입니다. 락은 뮤텍스를 이용하여 크리티컬 섹션(critical section)에 대한 접근을 제어합니다. 다음은 뮤텍스와 락을 사용하는 예제 코드입니다.
#include <iostream>
#include <thread>
#include <mutex>
std::mutex mtx;
void sharedResourceAccess() {
// 락을 이용하여 크리티컬 섹션에 대한 접근을 제어
std::lock_guard<std::mutex> lock(mtx);
// 공유 자원에 대한 작업을 수행
// ...
}
int main() {
std::thread t1(sharedResourceAccess);
std::thread t2(sharedResourceAccess);
t1.join();
t2.join();
return 0;
}
2. 원자적 연산
C++11부터는 원자적 연산(atomic operations)을 위한 표준 라이브러리가 제공됩니다. 이를 사용하여 스레드 안전한 프로그래밍을 구현할 수 있습니다. 원자적 연산은 여러 스레드가 동시에 접근해도 안전한 연산을 수행하도록 보장합니다. 다음은 원자적 연산을 사용하는 예제 코드입니다.
#include <iostream>
#include <thread>
#include <atomic>
std::atomic<int> sharedValue(0);
void updateSharedValue() {
sharedValue.fetch_add(1, std::memory_order_relaxed);
}
int main() {
std::thread t1(updateSharedValue);
std::thread t2(updateSharedValue);
t1.join();
t2.join();
std::cout << "Shared value: " << sharedValue << std::endl;
return 0;
}
3. 스레드 로컬 저장소
스레드 로컬 저장소(thread-local storage)를 사용하여 각 스레드마다 독립적인 저장소를 생성할 수 있습니다. 이를 통해 각 스레드가 독립적으로 데이터를 관리하고 공유하는 것을 피할 수 있습니다. 다음은 스레드 로컬 저장소를 사용하는 예제 코드입니다.
#include <iostream>
#include <thread>
#include <vector>
thread_local int threadId;
void threadFunction() {
threadId++;
std::cout << "Thread ID: " << threadId << std::endl;
}
int main() {
std::vector<std::thread> threads;
for (int i = 0; i < 5; i++) {
threads.push_back(std::thread(threadFunction));
}
for (auto &t : threads) {
t.join();
}
return 0;
}
결론
C++에서의 스레드 안전한 프로그래밍을 위해 뮤텍스와 락, 원자적 연산, 스레드 로컬 저장소 등 다양한 패턴과 도구를 이용할 수 있습니다. 이러한 패턴과 도구들을 적절히 활용하여 안정적이고 효율적인 멀티스레딩 프로그램을 개발할 수 있습니다.
참조:
- https://en.cppreference.com/w/cpp/thread
- https://en.cppreference.com/w/cpp/atomic
본문 끝.