동시성 디자인 패턴
Thread Pool 패턴
섹션 제목: “Thread Pool 패턴”스레드 생성/삭제 비용을 줄이기 위해 스레드를 미리 생성해두고 재사용합니다.
class ThreadPool { vector<thread> workers; queue<function<void()>> tasks; mutex mtx; condition_variable cv; bool stop = false;
public: ThreadPool(size_t n) { for (size_t i = 0; i < n; i++) { workers.emplace_back([this] { while (true) { function<void()> task; { unique_lock lock(mtx); cv.wait(lock, [this] { return stop || !tasks.empty(); }); if (stop && tasks.empty()) return; task = move(tasks.front()); tasks.pop(); } task(); } }); } }
template<class F> auto submit(F&& f) -> future<invoke_result_t<F>> { auto pkg = make_shared<packaged_task<invoke_result_t<F>()>>(forward<F>(f)); future result = pkg->get_future(); { lock_guard lock(mtx); tasks.emplace([pkg] { (*pkg)(); }); } cv.notify_one(); return result; }
~ThreadPool() { { lock_guard lock(mtx); stop = true; } cv.notify_all(); for (auto& w : workers) w.join(); }};
// 사용ThreadPool pool(4);auto fut = pool.submit([] { return 42; });cout << fut.get();Producer-Consumer 패턴
섹션 제목: “Producer-Consumer 패턴”생산자와 소비자를 분리해 작업 속도 차이를 버퍼로 흡수합니다.
template<typename T>class BoundedChannel { queue<T> buf; const size_t cap; mutex mtx; condition_variable not_full, not_empty;
public: BoundedChannel(size_t cap) : cap(cap) {}
void send(T item) { unique_lock lock(mtx); not_full.wait(lock, [this] { return buf.size() < cap; }); buf.push(move(item)); not_empty.notify_one(); }
T recv() { unique_lock lock(mtx); not_empty.wait(lock, [this] { return !buf.empty(); }); T item = move(buf.front()); buf.pop(); not_full.notify_one(); return item; }};
// 사용BoundedChannel<int> ch(10);thread producer([&]{ for(int i=0;i<100;i++) ch.send(i); });thread consumer([&]{ for(int i=0;i<100;i++) cout << ch.recv() << '\n'; });Monitor Object 패턴
섹션 제목: “Monitor Object 패턴”객체 내부 상태를 뮤텍스로 보호하고, 조건 변수로 대기/신호를 처리합니다.
class SharedCounter { int value = 0; mutable mutex mtx; condition_variable cv;
public: void increment() { lock_guard lock(mtx); ++value; cv.notify_all(); }
// value가 threshold 이상이 될 때까지 블록 void waitUntil(int threshold) { unique_lock lock(mtx); cv.wait(lock, [&] { return value >= threshold; }); }
int get() const { lock_guard lock(mtx); return value; }};Read-Write Lock 패턴
섹션 제목: “Read-Write Lock 패턴”읽기는 동시에 허용하고, 쓰기는 독점적으로 처리합니다.
#include <shared_mutex>
class RWCache { unordered_map<string, string> data; mutable shared_mutex mtx;
public: // 여러 스레드가 동시에 읽기 가능 optional<string> get(const string& key) const { shared_lock lock(mtx); auto it = data.find(key); return it != data.end() ? optional{it->second} : nullopt; }
// 쓰기는 독점 void set(const string& key, string value) { unique_lock lock(mtx); data[key] = move(value); }};Double-Checked Locking (싱글턴)
섹션 제목: “Double-Checked Locking (싱글턴)”초기화 비용을 줄이기 위한 지연 초기화 패턴입니다.
class Singleton { static atomic<Singleton*> instance; static mutex mtx;
Singleton() = default;
public: static Singleton* getInstance() { Singleton* p = instance.load(memory_order_acquire); if (!p) { lock_guard lock(mtx); p = instance.load(memory_order_relaxed); if (!p) { p = new Singleton(); instance.store(p, memory_order_release); } } return p; }};
// 더 간단한 C++11 방식 (권장)Singleton& getInstance() { static Singleton inst; // 스레드 안전 초기화 보장 (C++11) return inst;}Active Object 패턴
섹션 제목: “Active Object 패턴”비동기 메서드 호출을 큐에 쌓고 별도 스레드에서 순차 실행합니다.
class ActiveLogger { BoundedChannel<string> queue_{100}; thread worker_;
public: ActiveLogger() : worker_([this] { while (true) { string msg = queue_.recv(); if (msg == "__STOP__") break; cout << "[LOG] " << msg << '\n'; } }) {}
void log(string msg) { queue_.send(move(msg)); }
~ActiveLogger() { queue_.send("__STOP__"); worker_.join(); }};패턴 비교
섹션 제목: “패턴 비교”| 패턴 | 문제 | 핵심 아이디어 |
|---|---|---|
| Thread Pool | 스레드 생성 비용 | 스레드 재사용 |
| Producer-Consumer | 속도 불일치 | 버퍼로 분리 |
| Monitor Object | 공유 상태 보호 | 뮤텍스 + 조건변수 |
| Read-Write Lock | 읽기 병렬성 | shared_mutex |
| Active Object | 비동기 처리 | 명령 큐 + 전용 스레드 |
- Thread Pool: 고정 스레드 수로 작업 처리,
future로 결과 수신 - Producer-Consumer:
BoundedChannel로 백프레셔 구현 - Monitor Object: 뮤텍스 + 조건변수 캡슐화로 안전한 공유 상태
- Read-Write Lock: 읽기 다수/쓰기 희소 시
shared_mutex활용 - C++11 지역 정적 변수는 스레드 안전 초기화 보장 — 싱글턴에 활용