哲学家干饭问题 C++

news/2024/5/17 7:08:07/文章来源:https://blog.csdn.net/qq_42896106/article/details/126917511

哲学家干饭问题 C++

img

哲学家就餐问题可以这样表述,假设有五位哲学家围坐在一张圆形餐桌旁,做以下两件事情之一:吃饭,或者思考。吃东西的时候,他们就停止思考,思考的时候也停止吃东西。餐桌上有五碗意大利面,每位哲学家之间各有一支餐叉。因为用一支餐叉很难吃到意大利面,所以假设哲学家必须用两支餐叉吃东西。他们只能使用自己左右手边的那两支餐叉。哲学家就餐问题有时也用米饭和五根筷子而不是意大利面和餐叉来描述,因为吃米饭必须用两根筷子。

这个问题不考虑意大利面有多少,也不考虑哲学家的胃有多大。假设两者都是无限大。

问题在于如何设计一套规则,使得在哲学家们在完全不交谈,也就是无法知道其他人可能在什么时候要吃饭或者思考的情况下,可以在这两种状态下永远交替下去。

使用忙等待获取资源

#include <iostream>
#include <thread>
#include <atomic>
#include <atomic>
#include <format>
#include <list>
#include <mutex>
#include <shared_mutex>
#include <execution>
#include <map>
#include <future>
#include <future>
#include <iostream>
#include <thread>
#include <utility>
#include <random>using namespace std;std::random_device rd_device;
std::mt19937_64 rd_engine(rd_device());//生成随机数
int generate_random_number(int min, int max)
{std::uniform_int_distribution<int> dist(min, max);return dist(rd_engine);
}void lock(std::atomic<int>& resource_number)
{while (resource_number);//忙等待resource_number = 1;
}void unlock(std::atomic<int>& resource_number)
{resource_number = 0;
}void feast_think(int n, std::atomic<int>& a, std::atomic<int>& b)
{while (true){int duration = generate_random_number(1000, 1500);std::cout << std::format("{} 思考{}ms \n", n, duration);std::this_thread::sleep_for(std::chrono::milliseconds(duration)); // 思考lock(a);std::cout << std::format("{} 获取a筷子\n", n);std::this_thread::sleep_for(std::chrono::milliseconds(1000));lock(b);std::cout << std::format("{} 获取b筷子\n", n);duration = generate_random_number(1000, 1500);std::cout << std::format("{} 干饭时间:{} ms \n", n, duration);std::this_thread::sleep_for(std::chrono::milliseconds(duration));unlock(b);unlock(a);std::cout << "\n\n";}
}int main(int argc, char* argv[])
{std::atomic<int> m1{0}, m2{0}, m3{0}, m4{0};std::thread t1{[&]() { feast_think(1, m1, m2); }};std::thread t2{[&]() { feast_think(2, m2, m3); }};std::thread t3{[&]() { feast_think(3, m3, m4); }};std::thread t4{[&]() { feast_think(4, m1, m4); }};t1.join();t2.join();t3.join();t4.join();
}

image-20220918133938698

我们使用lock|unlock获取和释放资源, 但是在这之间,线程调度可能会导致线程切换,出现一些难以发现的问题

优化使用忙等待获取资源

std::random_device rd_device;
std::mt19937_64 rd_engine(rd_device());//生成随机数
int generate_random_number(int min, int max)
{std::uniform_int_distribution<int> dist(min, max);return dist(rd_engine);
}void lock(std::atomic_flag& resource_flag)
{while (resource_flag.test_and_set());//忙等待
}void unlock(std::atomic_flag& resource_flag)
{resource_flag.clear();
}void feast_think(int n, std::atomic_flag& a, std::atomic_flag& b)
{while (true){int duration = generate_random_number(100, 200);std::cout << std::format("{} 思考{}ms \n", n, duration);std::this_thread::sleep_for(std::chrono::milliseconds(duration)); // 思考lock(a);std::cout << std::format("{} 获取a筷子\n", n);std::this_thread::sleep_for(std::chrono::milliseconds(100));lock(b);std::cout << std::format("{} 获取b筷子\n", n);duration = generate_random_number(100, 200);std::cout << std::format("{} 干饭时间:{} ms \n", n, duration);std::this_thread::sleep_for(std::chrono::milliseconds(duration));unlock(b);unlock(a);std::cout << "\n\n";}
}int main(int argc, char* argv[])
{std::atomic_flag m1, m2, m3, m4;std::thread t1{[&]() { feast_think(1, m1, m2); }};std::thread t2{[&]() { feast_think(2, m2, m3); }};std::thread t3{[&]() { feast_think(3, m3, m4); }};std::thread t4{[&]() { feast_think(4, m1, m4); }};t1.join();t2.join();t3.join();t4.join();
}

lock中使用循环浪费了大量的CPU,可以使用sleep_for短暂的暂停循环来节约CPU

image-20220918134956329

降低CPU占用率

添加

while (resource_flag.test_and_set()){std::this_thread::sleep_for(std::chrono::milliseconds(8));}

image-20220918135104626

CPU占用几乎为零

使用std::mutex

关于自旋锁与互斥量的区别:Waiting Thread - an overview | ScienceDirect Topics

简单说:自旋锁等待另一个线程,互斥量等待系统调度,所以互斥量不会占用大量的CPU周期,自旋锁会占用大量的CPU周期

void feast_think(int n, std::mutex& a, std::mutex& b)
{while (true){int duration = generate_random_number(100, 200);std::cout << std::format("{} 思考{}ms \n", n, duration);std::this_thread::sleep_for(std::chrono::milliseconds(duration)); // 思考a.lock();std::cout << std::format("{} 获取a筷子\n", n);std::this_thread::sleep_for(std::chrono::milliseconds(100));b.lock();std::cout << std::format("{} 获取b筷子\n", n);duration = generate_random_number(100, 200);std::cout << std::format("{} 干饭时间:{} ms \n", n, duration);std::this_thread::sleep_for(std::chrono::milliseconds(duration));b.unlock();a.unlock();std::cout << "\n\n";}
}int main(int argc, char* argv[])
{std::mutex m1, m2, m3, m4;std::thread t1{[&]() { feast_think(1, m1, m2); }};std::thread t2{[&]() { feast_think(2, m2, m3); }};std::thread t3{[&]() { feast_think(3, m3, m4); }};std::thread t4{[&]() { feast_think(4, m1, m4); }};t1.join();t2.join();t3.join();t4.join();
}

使用std::unique_lock以及std::defer_lock标志

我们可以使用一步操作,同时获取两个互斥量,这样就不会有死锁的风险

为了保证输出同步,添加一个mutex mo

using namespace std;std::random_device rd_device;
std::mt19937_64 rd_engine(rd_device());//生成随机数
int generate_random_number(int min, int max)
{std::uniform_int_distribution<int> dist(min, max);return dist(rd_engine);
}std::mutex mo;void feast_think(int n, std::mutex& a, std::mutex& b)
{while (true){int duration = generate_random_number(1000, 2000);{std::lock_guard<std::mutex> g(mo);std::cout << std::format("{} 思考{}ms \n", n, duration);}std::this_thread::sleep_for(std::chrono::milliseconds(duration)); // 思考std::unique_lock<std::mutex> ga(a, std::defer_lock);{std::lock_guard<std::mutex> g(mo);std::cout << std::format("{} 获取a筷子\n", n);}std::this_thread::sleep_for(std::chrono::milliseconds(1000));std::unique_lock<std::mutex> gb(b, std::defer_lock);std::lock(ga, gb);{std::lock_guard<std::mutex> g(mo);std::cout << std::format("{} 获取b筷子\n", n);}duration = generate_random_number(1000, 2000);{std::lock_guard<std::mutex> g(mo);std::cout << std::format("{} 干饭时间:{} ms \n", n, duration);}std::this_thread::sleep_for(std::chrono::milliseconds(duration));std::cout << "\n\n";}
}int main(int argc, char* argv[])
{std::mutex m1, m2, m3, m4;std::thread t1{[&]() { feast_think(1, m1, m2); }};std::thread t2{[&]() { feast_think(2, m2, m3); }};std::thread t3{[&]() { feast_think(3, m3, m4); }};std::thread t4{[&]() { feast_think(4, m1, m4); }};t1.join();t2.join();t3.join();t4.join();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_9904.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue2.0到3.0的过渡,setup,ref函数,reactive函数,计算属性computed

setup 1、Vue3.0的组件中所有用到的:数据、方法等等&#xff0c;均要配置在setup中&#xff0c;若要使用里面的数据&#xff0c;可以用return将其返回出来 2、若在setup中返回的是一个对象&#xff0c;则对象中的数据、方法、在模板中均可直接使用 例如 <template><di…

[Git] 系列三随意修改提交记录以及一些技巧

[Git] 系列三随意修改提交记录以及一些技巧 Author: Xin Pan Date: 2022.09.17 文章目录[Git] 系列三随意修改提交记录以及一些技巧整理提交记录未知提交号哈希值时怎么办&#xff1f;一些技巧本地栈式提交方法一方法二TagDescribe高级命令总结好了&#xff0c;大概总结好了。…

搭建游戏要选什么样的服务器?

服务器是游戏平台数据传输的重要载体&#xff0c;事关我们游戏创业发展的稳定性、安全性。那么&#xff0c;游戏平台搭建要选什么服务器&#xff1f;有什么参考指标&#xff1f;本文将带领大家一探究竟&#xff01; 首先是“游戏平台搭建要选择什么服务器”&#xff0c;我们可…

论文阅读_对比学习_SimCSE

英文题目&#xff1a;SimCSE: Simple Contrastive Learning of Sentence Embeddings 中文题目&#xff1a;SimSCE&#xff1a;用简单的对比学习提升句嵌入的质量 论文地址&#xff1a;https://export.arxiv.org/pdf/2104.08821.pdf 领域&#xff1a;自然语言处理&#xff0c;对…

Redis的基本使用

1.Redis简介 &#xff08;1&#xff09;什么是Redis ①Redis是一个基于内存的key-value结构数据库 ②基于内存存储&#xff0c;读写性能高 ③适合存储热点数据(热点商品、资讯、新闻) ④Redis是一个开源的内存中的数据结构存储系统&#xff0c;它可以用作&#xff1a;数据库、…

计组--存储系统

存储系统 思维导图&#xff1a; 存储器概述 存储器的分类 按在计算机中的作用(层次)分类 主存储器&#xff0c;简称主存(内存) 存放计算机运行期间所需的程序和数据&#xff0c;CPU可以直接对其进行访问。 辅助存储器&#xff0c;简称辅存(外存) 辅存的内容需要调入主存后才…

普中A6开发版——XPT2046四引脚切换测量(含详细教程以及原理图等资料)

文章目录一、简介二、原理图以及手册三、接线四、选择数码管芯片原理讲解五、代码一、简介 本文介绍了XPT2046的使用方法以及普中A6开发版的接线等&#xff0c;并从原理图以及手册中摘选了详细的介绍&#xff0c;充分理解其工作原理。XPT2046本来是一个电阻式触摸屏控制器&…

监控系统架构方案

前言 对于企业级服务器管理&#xff0c;站群管理&#xff0c;针对服务器的监控是非常必要的。 通常&#xff0c;在电脑出现卡死&#xff0c;或进程停止或被挂起的情况下&#xff0c;大家都会使用任务管理器查看进程情况。针对电脑流畅性或资源优化&#xff0c;通常会使用资源管…

物联网开发笔记(19)- 使用Micropython开发ESP32开发板之连接WIFI热点

我们的ESP32开发板是拥有WIFI和蓝牙功能的。这里我们先告诉大家如何将ESP32开发板连接到我们家里的无线路由器上&#xff0c;并和连接到家里无线路由器的一台电脑进行通讯。 一、环境 ESP32开发板Thonny IDEWin10网络调试助手工具 后面设备联网的基本信息&#xff1a;开发板IP…

网课答案查题方法详细步骤

网课答案查题方法详细步骤 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 题库&#xff1a;题库后台&#xff08;点击跳转&#…

Dobbo微服务项目实战(详细介绍+案例源码) - 1.项目介绍及环境配置

系列文章目录 项目介绍及环境配置 文章目录系列文章目录一、项目介绍1. 功能2. 技术选型3. 页面预览⑴. 登录⑵. 交友&#xff08;主页&#xff09;⑶. 探花⑷. 搜附件⑸. 桃花传音⑹. 测灵魂⑺. 圈子⑻. 消息⑼. 小视频⑽. 我的二、开发工具1. YAPI2. Android模拟器3. 调试工…

ElasticSearch 命令总结

目录0&#xff0c;ES 与关系型数据库类比1&#xff0c;查看集群信息2&#xff0c;查看索引信息3&#xff0c;创建索引1&#xff0c;创建索引2&#xff0c;重建索引4&#xff0c;文档相关操作1&#xff0c;查看文档2&#xff0c;写入文档3&#xff0c;更新文档4&#xff0c;删除…

上海亚商投顾:A股持续调整 券商成做空主力

上海亚商投顾前言&#xff1a;无惧大盘大跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 市场情绪三大指数今日低开低走&#xff0c;午后均跌超2%&#xff0c;证券、房地产、煤炭等板块跌幅居前。券商股全线下挫&am…

centos8升级宝塔导致的openssl系列问题

故事的发生是这样的&#xff0c;从前有座山 这个问题很古怪&#xff0c;起先是我把宝塔面板从7.9.3升级到7.9.4&#xff0c;结果升级后宝塔弹出 libk5crypto.so.3: undefined symbol: EVP_KDF_ctrl, version OPENSSL_1_1_1b 再后来就是重启的话&#xff0c;连ssh都进不去&…

第137篇 荷兰拍卖

介绍荷兰拍卖,并通过简化版Azuki荷兰拍卖代码,讲解如何通过荷兰拍卖发售 ERC721标准的NFT。 1.荷兰拍卖 荷兰拍卖(Dutch Auction)是一种特殊的拍卖形式。 亦称“减价拍卖”,它是指拍卖标的的竞价由高到低依次递减直到第一个竞买人应价(达到或超过底价)时击槌成交的一种…

05-Java面向对象

文章目录初识面向对象面向过程&面向对象回顾方法及加深对象的创建分析创建与初始化对象构造器详解构造器-无参&#xff08;默认&#xff09;构造器-有参创建对象内存分析(简易)面向对象的三大特征封装封装的作用封装演示继承继承示例SuperSuper注意点super VS this方法重写…

Linux 虚拟地址空间

目录 1、一段代码引出一个问题 运行结果&#xff1a; 讨论&#xff1a; 2、Linux下进程虚拟地址空间分布 3、什么是虚拟地址空间&#xff1f; 4、虚拟地址出现之前&#xff1a;进程直接访问物理内存 5、再述虚拟地址空间 虚拟地址空间结构体是如何进行区域划分的呢&…

HTTP1.x协议详解和HTTP2.0笔记

http协议的作用就是指定两个web应用&#xff0c;之间的一种规则&#xff0c;各种特点&#xff0c;管道化&#xff0c;io多路复用&#xff0c;缓存&#xff0c;状态码&#xff0c;都是基于协议之间的字段&#xff0c;和io之间的调度来实现 HTTP的诞生 1989 年 3 月 CERN&#x…

Linux运维笔记[2]-宝塔面板

宝塔面板 宝塔Linux面板是提升运维效率的服务器管理软件,支持一键LAMP/LNMP/集群/监控/网站/FTP/数据库/JAVA等100多项服务器管理功能。 有30个人的专业团队研发及维护,经过200多个版本的迭代,功能全,少出错且足够安全,已获得全球百万用户认可安装。 openEuler安装宝塔面板…

ElasticSearch(九)【SpringBoot整合】

九、SpringBoot整合Elasticsearch 9.1 基本环境配置 创建一个springboot工程springboot-elasticsearch在pom.xml导入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifac…