线程实现睡觉的理发师问题(mutex方式)

2023-10-10 00:59

本文主要是介绍线程实现睡觉的理发师问题(mutex方式),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

线程实现睡觉的理发师问题

    • 一、题目
    • 二、基础知识
        • (1)互斥
        • (2)互斥锁
        • (3)POSIX线程
    • 三、函数说明
        • (1)pthread_t
        • (2) pthread_create
        • (3) pthread_join
        • (4) pthread_mutex_t
        • (5) pthread_mutex_init
        • (6) pthread_mutex_lock
        • (7) pthread_mutex_unlock
        • (8) pthread_mutex_destroy
    • 四、代码

一、题目

理发师问题的描述:一个理发店接待室有n张椅子,工作室有1张椅子;没有顾客时,理发师睡觉;第一个顾客来到时,必须将理发师唤醒;顾客来时如果还有空座的话,他就坐在一个座位上等待;如果顾客来时没有空座位了,他就离开,不理发了;当理发师处理完所有顾客,而又没有新顾客来时,他又开始睡觉。

二、基础知识

(1)互斥

互斥是指散布在不同任务之间的若干程序片断,当某个任务运行其中一个程序片段时,其它任务就不能运行它们之中的任一程序片段,只能等到该任务运行完这个程序片段后才可以运行。

最基本的场景就是:一个公共资源同一时刻只能被一个进程或线程使用,多个进程或线程不能同时使用公共资源。
(2)互斥锁

互斥锁( m u t e x mutex mutex)是一种简单的加锁的方法来控制对共享资源的访问,每个对象都对应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。互斥锁只有两种状态,即上锁和解锁。
多个线程只有一把锁一个钥匙,谁上的锁就只有谁能开锁。当一个线程要访问一个共享变量时,先用锁把变量锁住,然后再操作,操作完了之后再释放掉锁。
当另一个线程也要访问这个变量时,发现这个变量被锁住了,无法访问,它就会一直等待,直到锁被解了,它再给这个变量上个锁,然后使用,使用完了再释放锁,以此进行。

(3)POSIX线程

POSIX线程 (英语:POSIX Threads,常被缩写为 P t h r e a d s Pthreads Pthreads 是POSIX的线程标准,定义了创建和操纵线程的一套API。
实现POSIX 线程标准的库常被称作 P t h r e a d s Pthreads Pthreads


三、函数说明

接下来的函数都是 P t h r e a d s Pthreads Pthreads定义的函数,其头文件都为pthread.h。


(1)pthread_t
typedef unsigned long int pthread_t		
sizeof(pthread_t) = 8

pthread_t用于声明线程ID。

(2) pthread_create
int pthread_create(pthread_t *tidp,const pthread_attr_t *attr,void *(*start_rtn)(void*),void *arg)
//函数原型

pthread_create是的创建线程的函数。
第一个参数为指向线程标识符的指针。第二个参数用来设置线程属性。第三个参数是线程运行函数的起始地址。最后一个参数是运行函数的参数。本次内容运行函数不需要参数,所以最后一个参数设为空指针。第二个参数我们也设为空指针,这样将生成默认属性的线程。
若线程创建成功,则返回0。若线程创建失败,则返回出错编号。

(3) pthread_join
 int pthread_join(pthread_t thread, void **retval)//函数原型

pthread_join()函数,以阻塞的方式等待 t h r e a d thread thread指定的线程结束。pthread_join使一个线程等待另一个线程结束
第一个参数 t h r e a d thread thread: 线程标识符,即线程 I D ID ID,标识唯一线程。第二个参数 r e t v a l retval retval: 用户定义的指针,用来存储被等待线程的返回值。
运行成功返回0;失败,返回的则是错误号。

(4) pthread_mutex_t

pthread_mutex_t m u t e x mutex mutex 数据类型

(5) pthread_mutex_init
int pthread_mutex_init(pthread_mutex_t *restrict_mutex,const pthread_mutexattr_t *restrict_attr)
//函数原型

pthread_mutex_init()函数是以动态方式创建互斥锁的。函数成功执行后,互斥锁被初始化为未锁住态。
参数restrict_attr指定了新建互斥锁的属性。如果参数restrict_attr N U L L NULL NULL,则使用默认的互斥锁属性,默认属性为快速互斥锁 。
函数成功完成之后会返回零,其他任何返回值都表示出现了错误。

(6) pthread_mutex_lock
int pthread_mutex_lock(pthread_mutex_t *mutex)
//函数原型

pthread_mutx_lock用于 m u t e x mutex mutex 所指向的互斥锁上锁。
在成功完成之后会返回零。其他任何返回值都表示出现了错误。

(7) pthread_mutex_unlock
int pthread_mutex_unlock(pthread_mutex_t *mutex)
//函数原型

pthread_mutex_unlock可以解除锁定 m u t e x mutex mutex 所指向的互斥锁。
在成功完成之后会返回零。其他任何返回值都表示出现了错误。

(8) pthread_mutex_destroy
int pthread_mutex_destroy(pthread_mutex_t *mutex)
//函数原型

pthread_mutex_destroy用于互斥锁销毁。
在执行成功后返回 0,否则返回错误码。

四、代码

#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<unistd.h>pthread_mutex_t mutex;	//创建mutex变量
int n;		//有n个椅子,即有n个空位
int num;	//有num个顾客
int flag =1;	//函数循环判断
void *customer(void *arg)
{while(flag){pthread_mutex_lock(&mutex);		//互斥锁上锁if(num < n){num++;		//顾客数num加一printf("有顾客进来,还剩下%d个椅子\n",n-num);pthread_mutex_unlock(&mutex);	//互斥锁解锁}else{pthread_mutex_unlock(&mutex);	//互斥锁解锁printf("没有椅子了,顾客离开\n");}}	
}void *barber(void *arg)
{while(flag){if(num > 0){pthread_mutex_lock(&mutex);		//互斥锁上锁num--;		//顾客数num减一printf("顾客离开,还剩下%d个椅子\n",n-num);pthread_mutex_unlock(&mutex);	//互斥锁解锁}}
}int main(void)
{printf("请输入椅子总数n:");scanf("%d",&n);num = 0;	//初始化顾客数num为0pthread_t Barber,Customer;pthread_mutex_init(&mutex,NULL);	//创建互斥锁pthread_create(&Barber,NULL,barber,NULL);	//创建线程pthread_create(&Customer,NULL,customer,NULL);sleep(1);	//主线程休息1秒,执行子线程.flag = 0; 	//1秒后,子线程停止执行.pthread_join(Barber,NULL);	//等待子线程pthread_join(Customer,NULL);pthread_mutex_destroy(&mutex);	//摧毁互斥锁
}	

运行结果:

✔ 由于 p t h r e a d pthread pthread库不是标准 L i n u x Linux Linux库, 所以出错。 在编译命令后面添加 − l p t h r e a d -lpthread lpthread
在这里插入图片描述在这里插入图片描述
分析:
首先是输入椅子总数,然后第 46 46 46行和第 47 47 47行创建了两个线程 B o s s Boss Boss C u s t m o e r Custmoer Custmoer;首先进入 b a r b e r barber barber函数,此时 f l a g = 1 flag=1 flag=1,所以 w h i l e while while会在这时一直执行,而 n u m = 0 num=0 num=0所以单单执行 w h i l e while while循环什么也没做。然后进入 c u s t o m e r customer customer函数 (此时函数 b a r b e r barber barber也在执行) ,只要顾客数 n u m num num比椅子数 n n n小,就会进入 i f if if语句,第 16 16 16行互斥锁上锁 (资源占用),有顾客进来,顾客数 n u m num num加一,执行完后再对互斥锁解锁。其中只要 n u m > 0 num>0 num>0也会对互斥锁进行解锁从而对资源的占用。所以,该程序总共有三个线程在执行,一个主线程和两个子线程,它们同时执行着,但是只要一个子进程对互斥锁上锁了,相当于另一个子进程只能等互斥锁解锁了才能执行 (资源释放),从而达到互斥的效果,而主线程执行了第 49 49 49行的代码主线程休眠了 1 1 1秒, 1 1 1秒后执行第 50 50 50行把 f l a g flag flag改为 0 0 0,相当于两个子线程都执行了空语句,第 51 51 51行和第 52 52 52行分别等待子线程结束,最后所有线程都结束,也就是程序运行结束。


需要转载请标明出处

这篇关于线程实现睡觉的理发师问题(mutex方式)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/176801

相关文章

好题——hdu2522(小数问题:求1/n的第一个循环节)

好喜欢这题,第一次做小数问题,一开始真心没思路,然后参考了网上的一些资料。 知识点***********************************无限不循环小数即无理数,不能写作两整数之比*****************************(一开始没想到,小学没学好) 此题1/n肯定是一个有限循环小数,了解这些后就能做此题了。 按照除法的机制,用一个函数表示出来就可以了,代码如下

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

购买磨轮平衡机时应该注意什么问题和技巧

在购买磨轮平衡机时,您应该注意以下几个关键点: 平衡精度 平衡精度是衡量平衡机性能的核心指标,直接影响到不平衡量的检测与校准的准确性,从而决定磨轮的振动和噪声水平。高精度的平衡机能显著减少振动和噪声,提高磨削加工的精度。 转速范围 宽广的转速范围意味着平衡机能够处理更多种类的磨轮,适应不同的工作条件和规格要求。 振动监测能力 振动监测能力是评估平衡机性能的重要因素。通过传感器实时监

缓存雪崩问题

缓存雪崩是缓存中大量key失效后当高并发到来时导致大量请求到数据库,瞬间耗尽数据库资源,导致数据库无法使用。 解决方案: 1、使用锁进行控制 2、对同一类型信息的key设置不同的过期时间 3、缓存预热 1. 什么是缓存雪崩 缓存雪崩是指在短时间内,大量缓存数据同时失效,导致所有请求直接涌向数据库,瞬间增加数据库的负载压力,可能导致数据库性能下降甚至崩溃。这种情况往往发生在缓存中大量 k