哲学家进餐问题pv_【jMiniLang】哲学家就餐问题
演示GIF(1.57MB),控制台输出效果(还有parser/vm/ui)自己实现:
信号量与PV原语
信号量为正数代表有空闲资源,为零与负数代表没有空闲资源。负数的绝对值代表等待的进程个数。信号量用(S,Q)表示,S是非负初值的整型变量,Q为进程等待队列。
P原语(阻塞):令S=S-1,若S>=0,进程继续执行,否则该进程变为等待状态,入队列Q
V原语(唤醒):令S=S+1,若S>0,进程继续执行,否则释放队列Q中第一个等待信号量的进程
实现互斥:令S=1。
用管道语义实现PV操作
当前jMiniOS支持的管道操作:create_pipe,创建管道,返回句柄
write_pipe,向管道中写入一个字符(非堵塞)
read_pipe,从管道中不断读取字符流(堵塞)
read_pipe_once,从管道中读取一个字符(堵塞)
destroy_pipe,销毁管道
用管道实现信号量:
var g_create_semaphore = func ~(name, n) {
if (call g_query_pipe(name)) {
return;
}
var pipe = call g_create_pipe(name);
call g_write_pipe(pipe, call g_string_rep("*", n));
return pipe;
};
export "g_create_semaphore";
var g_use_semaphore = func ~(name) {
return call g_create_pipe(name);
};
export "g_use_semaphore";
var g_destroy_semaphore = func ~(handle) {
call g_destroy_pipe_once(handle);
};
export "g_destroy_semaphore";
var g_lock_semaphore = func ~(handle) {
call g_read_pipe_once(handle);
};
export "g_lock_semaphore";
var g_unlock_semaphore = func ~(handle) {
call g_write_pipe(handle, "*");
};
export "g_unlock_semaphore";
关键:创建管道后,信号量的初始值=管道中存储的字符个数,即管道中的内容=信号量中可用资源。
P操作 = 向管道中写一字符
V操作 = 从管道中读一字符
Mutex实现,create_mutex = create_semaphore(1)
实现哲学家就餐问题
/* 哲学家就餐问题 */
var stage_philo = func ~() {
call word_typewrite("演示哲学家就餐问题! \n", 100);
var print_mutex = call g_create_mutex("philo_print");
foreach (var i : call g_range(1, 5)) {
call g_create_semaphore("fork_" + i, 1); // 每个叉最多能使用一次
}
var handles = [];
var philo = func ~(args) { // philo = 新建进程, args = 参数
var eat = call g_map_get(args, "eating");
var o = call g_map_get(args, "out");
var id = call g_map_get(args, "id");
var left_id = call g_map_get(args, "left_id"); // 左叉ID
var right_id = call g_map_get(args, "right_id"); // 右叉ID
var left = call g_use_semaphore("fork_" + left_id); // 左叉信号量
var right = call g_use_semaphore("fork_" + right_id); // 右叉信号量
call eat(o, "Philosophy#" + id + " ready");
foreach (var j : call g_range(1, 10)) { // 吃十次才饱
call g_lock_semaphore(left); // P左叉
call eat(o, "Philosophy#" + id + " is using fork#" + left_id);
call g_lock_semaphore(right); // P右叉
call eat(o, "Philosophy#" + id + " is using fork#" + right_id);
call eat(o, "Philosophy#" + id + " is eating. Process: " + j + "0%");
call g_unlock_semaphore(left); // V左叉
call g_unlock_semaphore(right); // V右叉
}
call eat(o, "Philosophy#" + id + " OK");
};
var eating = func ~(out, str) { // 输出
var pm = call g_use_mutex("philo_print");
call g_lock_mutex(pm);
foreach (var c : call g_range_string(str)) {
call g_write_pipe(out, c);
}
call g_write_pipe(out, '\n');
call g_task_sleep_ms(100);
call g_unlock_mutex(pm);
};
foreach (var j : call g_range(1, 5)) {
var args = {}; // 传参
call g_map_put(args, "eating", eating);
call g_map_put(args, "out", out);
call g_map_put(args, "id", j);
call g_map_put(args, "left_id", (j == 1) ? 5 : (j - 1));
call g_map_put(args, "right_id", (j == 5) ? 1 : (j + 1));
var h = call g_create_user_process_args(philo, args); // 新建进程
call g_array_add(handles, h);
call g_task_sleep(1);
}
call g_join_process_array(handles);
foreach (var k : call g_range(1, 5)) {
call g_destroy_semaphore(call g_use_semaphore("fork_" + k)); // 销毁
}
call g_destroy_mutex(call g_use_mutex("philo_print")); // 销毁
};
总结
从实例上看,哲学家不是同时开始就餐的,而是陆续就餐,同时就餐肯定会导致死锁问题。所以要有一定的防死锁和饥饿的解法,网上也有很多,有一种管程的解法。
例子中每个哲学家不会知道其他哲学家的状态,因此肯定会有缺陷啦,假如先让A就餐,然后A吃完再唤醒其他人,那就好办了。
唤醒语义,好东西,想想linux中的进程状态实现,soga。因此,我们的程序除了要支持lock/unlock之外,还要支持wait/notify,下回分解。
===========================================
0903 更新:用管程实现(jMiniLang语言)
/* 哲学家就餐问题 - 管程解决 */
var stage_philo2 = func ~() {
call word_typewrite("【管程】演示哲学家就餐问题! \n", 100);
call g_create_mutex("philo_print");
call g_create_mutex("philo_monitor");
var states = [];
call g_array_add(states, g_null);
foreach (var i : call g_range(1, 5)) {
call g_create_semaphore("philo_" + i, 1); // 记录每个哲学家的状态(管程)
call g_array_add(states, "thinking"); // 开始时哲学家都在思考
}
var handles = [];
var philo2 = func ~(args) {
var eat = call g_map_get(args, "eating"); // 拿参数
var states = call g_map_get(args, "states");
var o = call g_map_get(args, "out");
var id = call g_map_get(args, "id");
var left_id = call g_map_get(args, "left_id");
var right_id = call g_map_get(args, "right_id");
var monitor = call g_use_mutex("philo_monitor");
call eat(o, "Philosophy#" + id + " ready");
var enter = func ~() { // 进入临界区
var monitor = call g_use_mutex("philo_monitor");
call g_lock_mutex(monitor); // mutex自带等待队列
call g_printdn("Philosophy#" + id + " entered critical section");
};
var leave = func ~() { // 离开临界区
call g_printdn("Philosophy#" + id + " leaved critical section");
var monitor = call g_use_mutex("philo_monitor");
call g_unlock_mutex(monitor);
};
var wait = func ~(_id) { // 等待信号
var sem = call g_use_semaphore("philo_" + _id);
call g_printdn("Philosophy#" + _id + " waiting");
call g_lock_mutex(sem); // semaphore自带等待队列
};
var signal = func ~(_id) { // 发出信号
var sem = call g_use_semaphore("philo_" + _id);
call g_unlock_mutex(sem);
call g_printdn("Philosophy#" + _id + " received signal");
};
var test = func ~(_id) { // 测试哲学家是否具备进餐条件
var _left_id = (_id == 1) ? 5 : (_id - 1);
var _right_id = (_id == 5) ? 1 : (_id + 1);
if ((call g_array_get(states, _left_id) != "eating") && // 如果左右都不在进餐
(call g_array_get(states, _right_id) != "eating") &&
(call g_array_get(states, _id) == "hungry")) { // 且自己为饥饿状态
call signal(_id); // 发出就餐信号
} else {
call g_printdn("Test failed. #" + _left_id + ": " + call g_array_get(states, _left_id) +
", #" + _right_id + ": " + call g_array_get(states, _right_id) + ", #" +
_id + ": " + call g_array_get(states, _id));
}
};
var pickup = func ~() { // 拿起叉子
call enter();
call g_array_set(states, id, "hungry"); // 设置状态是饥饿
call test(id); // 看看自己能否用餐
call leave();
if (call g_array_get(states, id) != "eating") { // 如果尝试失败
call wait(id); // 等待
call g_array_set(states, id, "eating"); // 设置为进餐状态
} // 这里设置状态不会冲突,因为pickup只能由一个哲学家调用
};
var putdown = func ~() { // 放下叉子
call enter();
call g_array_set(states, id, "thinking"); // 设置状态是思考
call test(left_id); // 测试左边的哲学家可否就餐
call test(right_id); // 测试右边的哲学家可否就餐
call leave();
};
foreach (var j : call g_range(1, 10)) {
call eat(o, "Philosophy#" + id + " is thinking");
call pickup();
call eat(o, "Philosophy#" + id + " is eating. Process: " + j + "0%");
call putdown();
}
call eat(o, "Philosophy#" + id + " OK");
};
var eating = func ~(out, str) {
var pm = call g_use_mutex("philo_print");
call g_lock_mutex(pm);
foreach (var c : call g_range_string(str)) {
call g_write_pipe(out, c);
}
call g_write_pipe(out, '\n');
call g_task_sleep_ms(100);
call g_unlock_mutex(pm);
};
foreach (var j : call g_range(1, 5)) {
var args = {};
call g_map_put(args, "eating", eating);
call g_map_put(args, "states", states);
call g_map_put(args, "out", out);
call g_map_put(args, "id", j);
call g_map_put(args, "left_id", (j == 1) ? 5 : (j - 1));
call g_map_put(args, "right_id", (j == 5) ? 1 : (j + 1));
var h = call g_create_user_process_args(philo2, args); // fork
call g_array_add(handles, h);
}
call g_join_process_array(handles);
foreach (var k : call g_range(1, 5)) {
call g_destroy_semaphore(call g_use_semaphore("fork_" + k));
}
call g_destroy_mutex(call g_use_mutex("philo_print"));
};
从输出来看,哲学家就餐的顺序是1->3->5->2->4,是非常有规律的、重复的、完美的顺序,这说明了管程的使用是非常有效的,同时证明了wait/signal机制的优越性:降低了锁的竞争程度(引入休眠),同时唤醒机制保证了进程同步中各进程的运行顺序。也就是说,如果一开始就出现了1->3->5->2->4循环,那么这个循环就一直存在直到程序结束。
【深入】如果哲学家是6个、7个,那么就餐顺序又如何呢?
哲学家进餐问题pv_【jMiniLang】哲学家就餐问题相关推荐
- java 第六次实验_操作系统第六次实验报告——使用信号量解决哲学家进餐问题...
0 个人信息 张樱姿 201821121038 计算1812 1 实验目的 通过编程进一步了解信号量. 2 实验内容 在服务器上用Vim编写一个程序:使用信号量解决任一个经典PV问题,测试给出结果,并 ...
- 经典的同步/互斥问题—哲学家进餐
经典的同步/互斥问题-哲学家进餐 一.问题描述 设有五个哲学家共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五个碗和五只筷子,他们的生活方式是交替地进行思考和进餐.平时,一个哲学家进行思考,饥饿时 ...
- java 哲学家_Java哲学家进餐问题|多线程
Java实验三 多线程 哲学家进餐问题 5个哲学家共用一张圆桌,分别坐在周围的5张椅子上, 在圆桌上有5个碗和5只筷子(注意是5只筷子,不是5双), 碗和筷子交替排列.他们的生活方式是交替地进行思考( ...
- 操作系统:哲学家进餐问题
哲学家进餐问题 五个哲学家围着一张圆桌,每个哲学家面前放着食物.哲学家的生活有两种交替活动:吃饭以及思考.当一个哲学家吃饭时,需要先拿起自己左右两边的两根筷子,并且一次只能拿起一根筷子. 下面是一种错 ...
- 【操作系统实验】Linux环境下用进程实现哲学家进餐问题——C语言完整代码+详细实验报告
[注意]代码在文末,以下为详细实验报告 [实验目的] 以哲学家进餐问题为例,学习并熟悉Linux下进程通信.同步机制的具体实现方法,主要是了解并掌握信号量机制和避免死锁的使用方法,使得不会出现哲学 ...
- 操作系统 | OS 经典同步问题之生产者-消费者问题,哲学家进餐问题
小目录 1. 生产者-消费者问题 2. 哲学家进餐问题 3. 吸烟者问题 1. 生产者-消费者问题 生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语 ...
- Linux哲学家进餐杀死进程,100分跪求“哲学家就餐问题”在 Linux下运行的源代码(后缀名为.c)!!!...
如题. | 代码大致如下,当然不能直接使用,我没写P,V操作的函数. # define N 5 /* 哲学家数目 */ # define LEFT (i-1+N)%N /* i的左邻号码 */ # d ...
- 【操作系统】“哲学家进餐”问题
"哲学家进餐"问题 有五个哲学家,他们的生活方式是交替地进行思考和进餐.他们共用一张圆桌,分别坐在五张椅子上. 在圆桌上有五个碗和五支筷子,平时一个哲学家进行思考,饥饿时便试图取用 ...
- PV操作经典例题——哲学家进餐问题
哲学家进餐问题: 五个哲学家共用一张圆桌,分别坐在周围的五张椅子上,在桌子上有五只碗和五只筷子,他们的生活方式是交替地进行思考和进餐.平时,一个哲学家进行思考,饥饿时便试图取用其左右最靠近他的筷子,只 ...
最新文章
- AI、区块链和机器人:技术会让未来的工作发生什变化?
- python手机版iphone-python如何绘制iPhone手机图案?(代码示例)
- C++中比较大小的表达式中,小于号和大于号都是不能连着打的,要用连接起来
- 文档生成工具Sandcastle Help File Builder
- mysql 分组数据_MySQL基础之分组数据
- Kettle使用_0 Windows下安装图解
- (计算机组成原理)第二章数据的表示和运算-第二节7:详解C语言中的强制类型转换
- 百度EasyDL深度学习实战营,免费教你转型AI工程师!
- 一个两年Java的面试总结
- 重庆大学李婷婷计算机学院,北京大学青年研究中心赴我校考察调研
- Google 发布最新 IDC 能源消耗报告
- python绘制简单城市剪影图_Python之绘制个人足迹地图
- JAVA读取属性文件的几种方法
- 草根站长建站需要掌握或者了解的5种技术
- MKVToolNix v72.0 MKV视频封装工具
- stay here forever中文歌词
- 时间展示 星期几时英文需要显示成中文问题解决
- Chrome浏览器上集成IE内核方案
- LED亮5秒灭5秒C语言程序代码,单片机【木仓示申吧】_百度贴吧
- mysql数据库select语句用法_mysql数据库select查询语句简单用法
热门文章
- Lenovo ideapad 15isk y700 固态限速问题的解决办法
- 字符编码问题三个不可见的字符(0xEF-0xBB-0xBF,即BOM)
- 解决方案:Zotero实现参考文献中英文混排,将英文文献中的“等”转成“et al.”
- 《数据结构课程实践》_02_隐式图的搜索问题_实现
- 计算机专业知识串讲,计算机基础知识串讲
- 计算机电路基础综合题,计算机电路基础作业考试习题.doc
- Android系统Camera图片反转的一个问题
- android saf小结
- (FMD)辉芒MCU开发指南
- collections, time, queue的应用