C++标准线程库之哲学家就餐问题

xiaoxiao2025-11-14  17

哲学家就餐问题是多线程中著名的一个问题,经过前面三章的学习,可以使用多线程来模拟下这个问题了。 问题是这样的,有5个哲学家围着一个小圆餐桌坐了下来,但是桌上只有5根筷子(注意是根),每个哲学家只有全抢到左右手边的筷子才能吃东西。抢到2根筷子的哲学家过1秒后把筷子放回原位置,继续游戏。当只抢到1根,另一手的筷子被其他哲学家抢走时,就放下手中的筷子。 为了简化问题,所有哲学家都是先抢左手,在抢右手的,有兴趣可以自己去实现随机版的,差不多。

1. 问题分析

首先,每个哲学家都应该是一个线程;其次,筷子是被共享的资源,所有每个筷子都应该被互斥量保护;很明显,1个互斥量是不够保护5个资源的,至少需要5个;因为需要判断能否上锁,互斥量的lock不能满足要求,而try_lock是可以的;吃完等待1秒继续。

2. 代码实现

#include <iostream> #include <exception> #include <mutex> #include <vector> #include <string> #include <algorithm> #include <functional> #include <thread> #include <chrono> std::mutex outputMutex; class Chopsticks { int _i; std::mutex _mutex; public: explicit Chopsticks() : _i(0) {} void seti(int i) { _i = i; } friend std::ostream& operator <<(std::ostream& out, const Chopsticks& chopsticks); // 使用筷子就是上锁 bool use() throw(std::system_error) { try { return _mutex.try_lock(); } catch(std::system_error& e) { throw e; } } // 放下筷子就是解锁 void push_down() throw(std::system_error) { try { return _mutex.unlock(); } catch(std::system_error& e) { throw e; } } }; std::ostream& operator <<(std::ostream& out, const Chopsticks& chopsticks) { out << " 筷子: " << chopsticks._i << " "; return out; } class Philosopher { int _i; // 拿某个筷子 bool _take(Chopsticks& chopsticks) { if(chopsticks.use()) { std::lock_guard<std::mutex> lock(outputMutex); std::cout << "哲学家: " << _i << "拿到" << chopsticks << std::endl; fflush(stdout); return true; } else { std::lock_guard<std::mutex> lock(outputMutex); std::cout << "抢夺失败, 哲学家: " << _i << chopsticks << std::endl; fflush(stdout); return false; } } // 抢夺 void _seizure(Chopsticks& lc, Chopsticks& rc) { while(true) { { std::lock_guard<std::mutex> lock(outputMutex); std::cout << "哲学家: " << _i << " 开始抢筷子..." << std::endl; fflush(stdout); } // 先拿左边,在那右边 if(_take(lc)) { if(_take(rc)) { std::lock_guard<std::mutex> lock(outputMutex); std::cout << "哲学家: " << _i << "吃饭" << std::endl; std::cout << "哲学家: " << _i << "放下" << rc << std::endl; fflush(stdout); rc.push_down(); } std::lock_guard<std::mutex> lock(outputMutex); std::cout << "哲学家: " << _i << "放下" << lc << std::endl; fflush(stdout); lc.push_down(); } // 休息1s std::this_thread::sleep_for(std::chrono::duration<double>(1)); } } public: explicit Philosopher() : _i(0) {} void seti(int i) { _i = i; } // 可调用的 void operator() (Chopsticks& lc, Chopsticks& rc) { _seizure(lc, rc); } }; int main() { // 5个哲学家,5个筷子 std::vector<Philosopher> philosophers(5); std::vector<Chopsticks> chopsticks(5); std::vector<std::thread> threads; // 设置序号 for(int i = 0; i < 5; ++ i) { philosophers.at(i).seti(i + 1); chopsticks.at(i).seti(i + 1); } // 开启线程 for(int i = 0; i < 4; ++ i) { std::thread thread(philosophers.at(i), std::ref(chopsticks.at(i)), std::ref(chopsticks.at(i + 1))); threads.push_back(std::move(thread)); } std::thread thread(philosophers.at(4), std::ref(chopsticks.at(4)), std::ref(chopsticks.at(0))); threads.push_back(std::move(thread)); std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); return 0; }

3. 总结

虽然代码不多一百来行,但是其用到了线程创建,线程管理,互斥量等知识,细细品味能加深对线程的理解。

4. 相关系列

C++标准线程库之入门C++标准线程库之当前线程管理C++标准线程库之共享资源
转载请注明原文地址: https://www.6miu.com/read-5039645.html

最新回复(0)