
一、读者-写者问题核心允许多个读者并发进入写者独占。经典读者优先解法用一个read_count 两把锁实现。#include iostream #include thread #include mutex #include condition_variable #include vector #include chrono class ReadWriteLock { std::mutex mtx_; std::condition_variable cv_; int readers_ 0; // 当前正在读的读者数 bool writer_active_ false; // 是否有写者在写 public: void read_lock() { std::unique_lockstd::mutex lk(mtx_); cv_.wait(lk, [this]{ return !writer_active_; }); // 无写者才能进 readers_; } void read_unlock() { std::unique_lockstd::mutex lk(mtx_); if (--readers_ 0) cv_.notify_all(); // 最后一个读者离开唤醒写者 } void write_lock() { std::unique_lockstd::mutex lk(mtx_); cv_.wait(lk, [this]{ return !writer_active_ readers_ 0; }); writer_active_ true; } void write_unlock() { std::unique_lockstd::mutex lk(mtx_); writer_active_ false; cv_.notify_all(); } }; ReadWriteLock rw; int shared_data 0; void reader(int id) { for (int i 0; i 3; i) { rw.read_lock(); std::cout Reader id reads: shared_data std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(50)); rw.read_unlock(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } void writer(int id) { for (int i 0; i 2; i) { rw.write_lock(); shared_data; std::cout Writer id writes: shared_data std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(100)); rw.write_unlock(); std::this_thread::sleep_for(std::chrono::milliseconds(50)); } } int main() { std::vectorstd::thread ts; for (int i 1; i 3; i) ts.emplace_back(reader, i); for (int i 1; i 2; i) ts.emplace_back(writer, i); for (auto t : ts) t.join(); return 0; }二、睡眠理发师问题理发店有一个理发师、N 把等候椅。无顾客时理发师睡觉顾客来时若椅子满则离开否则唤醒理发师#include iostream #include thread #include mutex #include condition_variable #include queue #include chrono constexpr int MAX_CHAIRS 5; // 等候椅数量 class BarberShop { std::mutex mtx_; std::condition_variable cv_customer_, cv_barber_; std::queueint waiting_; // 等候的顾客 id bool barber_sleeping_ true; bool customer_ready_ false; bool haircut_done_ false; bool shop_open_ true; public: // 顾客线程 bool customer(int id) { std::unique_lockstd::mutex lk(mtx_); if (waiting_.size() MAX_CHAIRS) { std::cout Customer id leaves (no chair)\n; return false; } waiting_.push(id); if (barber_sleeping_) { barber_sleeping_ false; cv_barber_.notify_one(); // 叫醒理发师 } // 等待轮到自己理发 cv_customer_.wait(lk, []{ return !shop_open_ || waiting_.front() id; }); if (!shop_open_) return false; waiting_.pop(); customer_ready_ true; cv_barber_.notify_one(); // 等待理发完成 cv_customer_.wait(lk, []{ return haircut_done_; }); haircut_done_ false; std::cout Customer id done\n; return true; } // 理发师线程 void barber() { while (true) { std::unique_lockstd::mutex lk(mtx_); cv_barber_.wait(lk, []{ return !shop_open_ || customer_ready_ || !waiting_.empty(); }); if (!shop_open_ waiting_.empty() !customer_ready_) return; if (!customer_ready_ !waiting_.empty()) { cv_customer_.notify_all(); // 让队首顾客进入 } cv_barber_.wait(lk, []{ return customer_ready_; }); lk.unlock(); std::cout Barber is cutting hair...\n; std::this_thread::sleep_for(std::chrono::milliseconds(200)); lk.lock(); customer_ready_ false; haircut_done_ true; cv_customer_.notify_all(); if (waiting_.empty()) barber_sleeping_ true; } } void close() { std::unique_lockstd::mutex lk(mtx_); shop_open_ false; cv_barber_.notify_all(); cv_customer_.notify_all(); } }; BarberShop shop; void barber_thread() { shop.barber(); } void customer_thread(int id) { shop.customer(id); } int main() { std::thread b(barber_thread); std::vectorstd::thread cs; for (int i 1; i 8; i) { cs.emplace_back(customer_thread, i); std::this_thread::sleep_for(std::chrono::milliseconds(50)); } for (auto t : cs) t.join(); shop.close(); b.join(); return 0; }三、吸烟者问题代理每次随机放两种原料在桌上拥有第三种原料的吸烟者才能卷烟并抽完再通知代理继续#include iostream #include thread #include mutex #include condition_variable #include chrono #include cstdlib // 三种原料0烟草, 1纸, 2火柴 // 吸烟者 i 拥有原料 i缺少其他两种 class Smokers { std::mutex mtx_; std::condition_variable cv_smoker_[3], cv_agent_; bool on_table_[3] {false, false, false}; bool agent_waiting_ true; public: void agent() { for (int round 0; round 5; round) { std::unique_lockstd::mutex lk(mtx_); int a std::rand() % 3; int b (a 1 std::rand() % 2) % 3; // 取不同于 a 的另一种 on_table_[a] on_table_[b] true; std::cout Agent puts ingredients a b \n; cv_smoker_[a].notify_all(); cv_smoker_[b].notify_all(); cv_agent_.wait(lk, []{ return agent_waiting_; }); agent_waiting_ false; lk.unlock(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } } void smoker(int id) { while (true) { std::unique_lockstd::mutex lk(mtx_); cv_smoker_[id].wait(lk, []{ return on_table_[(id1)%3] on_table_[(id2)%3]; }); on_table_[0] on_table_[1] on_table_[2] false; std::cout Smoker id rolls smokes\n; lk.unlock(); std::this_thread::sleep_for(std::chrono::milliseconds(50)); lk.lock(); agent_waiting_ true; cv_agent_.notify_one(); } } }; int main() { std::srand(std::time(nullptr)); Smokers s; std::thread agt(Smokers::agent, s); std::vectorstd::thread sms; for (int i 0; i 3; i) sms.emplace_back(Smokers::smoker, s, i); agt.join(); // 演示用实际中吸烟者线程会一直循环 return 0; }四、屏障同步Barrier用于多线程各自完成一段工作后等所有线程都到达某点再继续。C20 才有std::barrierC11 需要自己实现#include iostream #include thread #include mutex #include condition_variable #include vector class Barrier { std::mutex mtx_; std::condition_variable cv_; int count_, total_; int generation_ 0; public: explicit Barrier(int n) : count_(n), total_(n) {} void wait() { std::unique_lockstd::mutex lk(mtx_); int gen generation_; if (--count_ 0) { count_ total_; generation_; cv_.notify_all(); } else { cv_.wait(lk, []{ return generation_ ! gen; }); } } }; Barrier barrier(4); void worker(int id) { for (int round 0; round 3; round) { std::cout Worker id phase round done\n; barrier.wait(); // 所有线程都到齐才进入下一阶段 } } int main() { std::vectorstd::thread ts; for (int i 0; i 4; i) ts.emplace_back(worker, i); for (auto t : ts) t.join(); return 0; }小结问题核心难点常用原语读者-写者读写互斥、读读并发、避免写者饥饿mutex condition_variable 计数器睡眠理发师有限等待队列、唤醒机制mutex 两个 condition_variable 队列吸烟者资源组合匹配、代理与吸烟者的握手mutex 多个 condition_variable屏障等待全员到齐再继续mutex condition_variable 代际计数其中读者-写者和睡眠理发师是 Tanenbaum 书中明确讲到的两大经典 IPC 问题83.136.203吸烟者和屏障属于常见补充。这些模型也对应后续章节讨论的进程通信与死锁章节——哲学家就餐用于演示死锁避免睡眠理发师用于演示资源分配同步读者-写者用于演示并发控制策略建议对照原书一起看效果更好。