操作系统:线程同步

操作系统:线程同步

使用Linux无名信号量实现了读写者线程的互斥和同步。

实验环境

  • 环境:Linux
  • 语言:C
  • CMake:3.17.1
  • GCC:7.5.0
  • IDE:Clion 2020.3.1

实验目标

  1. 理解进程同步的两种制约关系:互斥与同步。
  2. 掌握利用记录型信号量解决进程同步问题的方法。
  3. 加深对进程同步控制的理解。

实验内容

以“生产者-消费者模型”为基础,在Linux环境下创建一个控制进程,在该进程中创建读者写者线程各一个,模拟生产者和消费者,它们使用N个不同的缓冲区(N为一个确定的数值,本实验中取N=16)。写者线程写入数据,然后将数据放置在空缓冲区中供读者线程读取。读者线程从缓冲区中获得数据,然后释放缓冲区。当写者线程写入数据时,如果没有空缓冲区可用,那么写者线程必须等待读者线程释放出一个空缓冲区。当读者线程读取数据时,如果没有满的缓冲区,那么读者线程将被阻塞,直到缓冲区被写者线程写满。

可以作如下的实验尝试,并观察和记录进程同步效果:

  1. 没有信号量时实现生产者线程与消费者线程互斥制约;
  2. 用一个互斥信号量mutex,用以阻止生产者线程和消费者线程同时操作缓冲区列表;
  3. 用一个信号量full,当生产者线程生产出一个物品时可以用它向消费者线程发出信号;用一个信号量empty,消费者线程释放出一个空缓冲区时可以用它向生产者线程发出信号。
  4. 同时使用2、3中的信号量,实现生产者-消费者的互斥与同步制约

实验过程和结果

函数介绍

pthread.h

#include <pthread.h>/* Create a new thread, starting with execution of START-ROUTINEgetting passed ARG.  Creation attributed come from ATTR.  The newhandle is stored in *NEWTHREAD.  */
extern int pthread_create (pthread_t *__restrict __newthread,const pthread_attr_t *__restrict __attr,void *(*__start_routine) (void *),void *__restrict __arg) __THROWNL __nonnull ((1, 3));/* Terminate calling thread.The registered cleanup handlers are called via exception handlingso we cannot mark this function with __THROW.*/
extern void pthread_exit (void *__retval) __attribute__ ((__noreturn__));/* Make calling thread wait for termination of the thread TH.  Theexit status of the thread is stored in *THREAD_RETURN, if THREAD_RETURNis not NULL.This function is a cancellation point and therefore not marked with__THROW.  */
extern int pthread_join (pthread_t __th, void **__thread_return);/* Obtain the identifier of the current thread.  */
extern pthread_t pthread_self (void) __THROW __attribute__ ((__const__));/* Thread identifiers.  The structure of the attribute type is notexposed on purpose.  */
typedef unsigned long int pthread_t;

semaphore.h

在 POSIX 标准中,信号量分两种,一种是无名信号量,一种是有名信号量。无名信号量一般用于线程间同步或互斥,而有名信号量一般用于进程间同步或互斥。它们的区别和管道及命名管道的区别类似,无名信号量则直接保存在内存中,而有名信号量要求创建一个文件。

无名信号量需要semaphore.h头文件,有名信号量则需要sys/sem.h头文件。本文实现线程间同步和互斥,故使用更为简便的无名信号量。

#include <semaphore.h>typedef union
{char __size[__SIZEOF_SEM_T];long int __align;
} sem_t;/* Initialize semaphore object SEM to VALUE.  If PSHARED then share itwith other processes.  */
extern int sem_init (sem_t *__sem, int __pshared, unsigned int __value)__THROW;/* Free resources associated with semaphore object SEM.  */
extern int sem_destroy (sem_t *__sem) __THROW;This function is a cancellation point and therefore not marked with__THROW.  */
extern int sem_wait (sem_t *__sem);/* Post SEM.  */
extern int sem_post (sem_t *__sem) __THROWNL;

CMake

# based on clion
cmake_minimum_required(VERSION 3.17)set(CMAKE_CXX_STANDARD 14)find_package(Threads REQUIRED)
add_executable(thread main.c ${SOURCE_FILES})
target_link_libraries(thread Threads::Threads)

信号量用于互斥与同步

在这里插入图片描述

在这里插入图片描述

一、读、写者互斥

创建读者写者void *Read(void *arg)void *Write(void *arg),由于只能有一个读者、一个写者,可知读者之间是互斥的、写者之间也是互斥的,故设置信号量sem_t read_semsem_t write_sem. 设置并初始化全局变量int buffer = 0;模拟缓冲区的写入和读出。设置并初始化int read_write_flag[2] = {1, 1};判断是否发生读写冲突。

//
// Created by Sylvan Ding on 2022/5/10.
//#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>#define N 16
#define MAX_PROCESS_NUM 100int buffer = 0;
int read_write_flag[2] = {1, 1};// sem
sem_t read_sem;
sem_t write_sem;void *Read(void *arg) {sem_wait(&read_sem);read_write_flag[0] = 0;int buf_temp;int duration = rand() % 4;pthread_t tid = pthread_self();printf("\033[32m[R] tid: %lu starts reading...\033[0m\n", tid);if (!read_write_flag[1])printf("\033[31m[R] tid: %lu conflicts with writing process!\033[0m\n", tid);// readbuf_temp = buffer;sleep(duration);buffer = 0;printf("[R] tid: %lu has read all %d buffers!\n", tid, buf_temp);if (buf_temp < N)printf("\033[33m[R] tid: %lu did not read entire 16 buffers!\033[0m\n", tid);read_write_flag[0] = 1;sem_post(&read_sem);return NULL;
}void *Write(void *arg) {sem_wait(&write_sem);read_write_flag[1] = 0;int res;int buf_temp;int size = rand() % 16 + 1;int duration = rand() % 5;pthread_t tid = pthread_self();printf("\033[32m[W] tid: %lu starts writing...\033[0m\n", tid);// writebuf_temp = buffer;res = size + buf_temp;sleep(duration);if (res > N)buffer = 16;elsebuffer = res;printf("[W] tid: %lu has written %d buffers, with %d buffers occupied!\n", tid, res > N ? N - buf_temp : size,buffer);read_write_flag[1] = 1;sem_post(&write_sem);return NULL;
}int main(int argc, char *argv[]) {srand(888);int res = 0;int randm = 0;void *(*target)(void *) =NULL;pthread_t myThreads[MAX_PROCESS_NUM];// init semif (sem_init(&read_sem, 0, 1) < 0) {printf("failed to init read_sem");exit(0);}if (sem_init(&write_sem, 0, 1) < 0) {printf("failed to init write_sem");exit(0);}for (int i = 0; i < MAX_PROCESS_NUM; ++i) {randm = rand() % 4;if (i % 2)target = Read;elsetarget = Write;res = pthread_create(&myThreads[i], NULL, target, NULL);if (res < 0) {printf("failed to create the thread!");exit(0);}sleep(randm);}void *thread_result = NULL;for (int i = 0; i < MAX_PROCESS_NUM; ++i)res = pthread_join(myThreads[i], &thread_result);// destroy semsem_destroy(&read_sem);sem_destroy(&write_sem);return 0;
}

在这里插入图片描述

二、读写者互斥

为实现读、写互斥,设置mutex信号量,编写void *Read1(void *arg)void *Write1(void *arg).

sem_t mutex;void *Read1(void *arg) {sem_wait(&mutex);Read(arg);sem_post(&mutex);return NULL;
}void *Write1(void *arg) {sem_wait(&mutex);Write(arg);sem_post(&mutex);return NULL;
}void all_sem_init() {// init semint res = 0;if (sem_init(&read_sem, 0, 1) < 0) {printf("failed to init read_sem");res = -1;}if (sem_init(&write_sem, 0, 1) < 0) {printf("failed to init write_sem");res = -1;}if (sem_init(&mutex, 0, 1) < 0) {printf("failed to init mutex");res = -1;}if (res)exit(0);
}void all_sem_des() {// destroy semsem_destroy(&read_sem);sem_destroy(&write_sem);sem_destroy(&mutex);
}int main(int argc, char *argv[]) {srand(888);int res = 0;int randm = 0;void *(*target)(void *) =NULL;pthread_t myThreads[MAX_PROCESS_NUM];all_sem_init();for (int i = 0; i < MAX_PROCESS_NUM; ++i) {randm = rand() % 4;if (i % 2)
//            target = Read;target = Read1;else
//            target = Write;target = Write1;res = pthread_create(&myThreads[i], NULL, target, NULL);if (res < 0) {printf("failed to create the thread!");exit(0);}sleep(randm);}void *thread_result = NULL;for (int i = 0; i < MAX_PROCESS_NUM; ++i)res = pthread_join(myThreads[i], &thread_result);all_sem_des();return 0;
}

在这里插入图片描述

三、读写者同步

为了解决在缓冲区未满时读进程就读出缓冲区的问题,设置full信号量。同时,在缓冲区写满时,阻塞写进程,设置empty信号量。二者均初始化为0.

sem_t full;
sem_t empty;void all_sem_init() {// init semint res = 0;// ...if (sem_init(&full, 0, 0) < 0) {printf("failed to init full");res = -1;}if (sem_init(&empty, 0, 0) < 0) {printf("failed to init empty");res = -1;}if (res)exit(0);
}void all_sem_des() {// destroy sem// ...sem_destroy(&full);sem_destroy(&empty);
}void *Read2(void *arg) {sem_wait(&full);Read(arg);sem_post(&empty);return NULL;
}void *Write2(void *arg) {Write(arg);if (buffer >= N) {sem_post(&full);sem_wait(&empty);}return NULL;
}int main(int argc, char *argv[]) {// ...for (int i = 0; i < MAX_PROCESS_NUM; ++i) {randm = rand() % 4;if (i % 2) {
//            target = Read;
//            target = Read1;target = Read2;} else {
//            target = Write;
//            target = Write1;target = Write2;}// ...
}

在这里插入图片描述

四、读写者同步且互斥

结合二、三的信号量,实现读写同步且互斥。

void *Read3(void *arg) {sem_wait(&full);sem_wait(&mutex);Read(arg);sem_post(&mutex);sem_post(&empty);return NULL;
}void *Write3(void *arg) {sem_wait(&mutex);Write(arg);sem_post(&mutex);if (buffer >= N) {sem_post(&full);sem_wait(&empty);}return NULL;
}

在这里插入图片描述

改进

因为只有一个读者、一个写者进程,并非多读写者,所以可以不用在main()中开多个线程,可以只创建read()write()两线程,然后在线程内部使用while循环去访问buffer. 这个工作就留给读者自己去实现啦~😄

附录(完整实验代码)

//
// Created by Sylvan Ding on 2022/5/10.
//#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>#define N 16
#define MAX_PROCESS_NUM 100int buffer = 0;
int read_write_flag[2] = {1, 1};// sem
sem_t read_sem;
sem_t write_sem;
sem_t mutex;
sem_t full;
sem_t empty;void *Read(void *arg) {sem_wait(&read_sem);read_write_flag[0] = 0;int buf_temp;int duration = rand() % 4;pthread_t tid = pthread_self();printf("\033[32m[R] tid: %lu starts reading...\033[0m\n", tid);if (!read_write_flag[1])printf("\033[31m[R] tid: %lu conflicts with writing process!\033[0m\n", tid);// readbuf_temp = buffer;sleep(duration);buffer = 0;printf("[R] tid: %lu has read all %d buffers!\n", tid, buf_temp);if (buf_temp < N)printf("\033[33m[R] tid: %lu did not read entire 16 buffers!\033[0m\n", tid);read_write_flag[0] = 1;sem_post(&read_sem);return NULL;
}void *Write(void *arg) {sem_wait(&write_sem);read_write_flag[1] = 0;int res;int buf_temp;int size = rand() % 16 + 1;int duration = rand() % 5;pthread_t tid = pthread_self();printf("\033[32m[W] tid: %lu starts writing...\033[0m\n", tid);// writebuf_temp = buffer;res = size + buf_temp;sleep(duration);if (res > N)buffer = 16;elsebuffer = res;printf("[W] tid: %lu has written %d buffers, with %d buffers occupied!\n", tid, res > N ? N - buf_temp : size,buffer);read_write_flag[1] = 1;sem_post(&write_sem);return NULL;
}void *Read1(void *arg) {sem_wait(&mutex);Read(arg);sem_post(&mutex);return NULL;
}void *Write1(void *arg) {sem_wait(&mutex);Write(arg);sem_post(&mutex);return NULL;
}void *Read2(void *arg) {sem_wait(&full);Read(arg);sem_post(&empty);return NULL;
}void *Write2(void *arg) {Write(arg);if (buffer >= N) {sem_post(&full);sem_wait(&empty);}return NULL;
}void *Read3(void *arg) {sem_wait(&full);sem_wait(&mutex);Read(arg);sem_post(&mutex);sem_post(&empty);return NULL;
}void *Write3(void *arg) {sem_wait(&mutex);Write(arg);sem_post(&mutex);if (buffer >= N) {sem_post(&full);sem_wait(&empty);}return NULL;
}void all_sem_init() {// init semint res = 0;if (sem_init(&read_sem, 0, 1) < 0) {printf("failed to init read_sem");res = -1;}if (sem_init(&write_sem, 0, 1) < 0) {printf("failed to init write_sem");res = -1;}if (sem_init(&mutex, 0, 1) < 0) {printf("failed to init mutex");res = -1;}if (sem_init(&full, 0, 0) < 0) {printf("failed to init full");res = -1;}if (sem_init(&empty, 0, 0) < 0) {printf("failed to init empty");res = -1;}if (res)exit(0);
}void all_sem_des() {// destroy semsem_destroy(&read_sem);sem_destroy(&write_sem);sem_destroy(&mutex);sem_destroy(&full);sem_destroy(&empty);
}int main(int argc, char *argv[]) {srand(888);int res = 0;int randm = 0;void *(*target)(void *) =NULL;pthread_t myThreads[MAX_PROCESS_NUM];all_sem_init();for (int i = 0; i < MAX_PROCESS_NUM; ++i) {randm = rand() % 4;if (i % 2) {
//            target = Read;
//            target = Read1;
//            target = Read2;target = Read3;} else {
//            target = Write;
//            target = Write1;
//            target = Write2;target = Write3;}res = pthread_create(&myThreads[i], NULL, target, NULL);if (res < 0) {printf("failed to create the thread!");exit(0);}sleep(randm);}void *thread_result = NULL;for (int i = 0; i < MAX_PROCESS_NUM; ++i)res = pthread_join(myThreads[i], &thread_result);all_sem_des();return 0;
}

版权声明:原创文章,转载请注明出处 ©️ Sylvan Ding

参考文献

  1. Linux系统编程——线程同步与互斥:POSIX无名信号量

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

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

相关文章

中国工程院2021年院士增选第二轮候选人名单公布

来源&#xff1a;先进制造业中国工程院2021年院士增选进入第二轮评审候选人名单&#xff08;按候选人姓名拼音排序&#xff09;未来智能实验室的主要工作包括&#xff1a;建立AI智能系统智商评测体系&#xff0c;开展世界人工智能智商评测&#xff1b;开展互联网&#xff08;城…

vsftpd部署流程和常见问题详解

vsftpd部署流程和常见问题详解 ⭐️ 网上关于在云服务器里配置vsftpd的文章鱼龙混杂&#xff0c;没有一篇是可以彻底解决问题的&#xff0c;有些问题虽简单&#xff0c;但也让初学者感到困惑。本文详细说明vsftpd的部署流程和一些常见问题的解决方法&#xff0c;详述用户创建过…

谷歌发布史上最强人类大脑「地图」,1.3亿个突触,在线可视3D神经元「森林」!...

来源&#xff1a;Google AI Blog编辑&#xff1a;yaxin, LQ &#xff08;新智元&#xff09;突触&#xff0c;是神经网络的「桥梁」。我们知道&#xff0c;人类大脑有860亿个神经元&#xff0c;因为有了突触&#xff0c;才可以把神经元上的电信号传递到下一个神经元。长久以来&…

智慧食堂项目策划书(商业计划书/立项计划)

金鹰物联智慧食堂项目策划书&#xff08;商业计划书/立项计划&#xff09; ©️ 金鹰物联项目组&#xff0c;转载请注明出处&#xff01; ⭐️ 技术部分请参考博文&#xff1a;基于YOLOv5的中式快餐店菜品识别系统 文章目录金鹰物联智慧食堂项目策划书&#xff08;商业计划…

pipelineDB学习笔记-2. Stream (流)

一、流的定义&#xff1a; 所谓的“流”&#xff08;stream&#xff09;在pipelineDB中是指那些被允许的数据库客服端推送到 Continuous View&#xff08;连续视图&#xff09; 的时序化数据的一种“抽象”。流中的每一个raw(数据列)或者event(事件)&#xff0c;看起来是和普通…

银行家算法之Python实现[操作系统实验]

银行家算法 银行家算法是著名的死锁避免算法&#xff0c;其思想是&#xff1a;把操作系统视为银行家&#xff0c;操作系统管理的资源相当于银行家管理的资金&#xff0c;进程向操作系统请求分配资源相当于用户向银行家贷款。操作系统按照银行家制定的规则为进程分配资源。进程…

城市智能化发展中,AI公司应该做什么?

来源&#xff1a;虎嗅APP题图来源:视觉中国在上海长宁区的部分区域&#xff0c;你会发现共享单车总是能够整整齐齐的排列&#xff0c;并且在每一个你需要的街道路边&#xff0c;都能找到空闲的单车&#xff0c;既不会车辆爆满&#xff0c;也不会无车可骑。这些城市细微之处的体…

getchar(),putchar()用法

1.getchar函数可以接收用户输入的多个字符&#xff0c;只用等用户按下回车键后&#xff0c;getchar()函数才会从键盘缓冲区依次读出字符&#xff1b; 2.getchar()和putchar()结合使用&#xff0c;可以将读取的字符输出到屏幕&#xff1b; 3.函数原型&#xff1a; int getchar…

[操作系统]页面置换算法实验及C++实现(OPT、FIFO、LRU)

虚拟内存页面置换算法实验&#xff08;OPT、FIFO、LRU&#xff09; 进程运行时&#xff0c;若其访问的页面不再内存中而需将其调入&#xff0c;但内存已无空闲空间时&#xff0c;就需要从内存中调出一页程序或数据&#xff0c;送入磁盘的对换区。选择调出页面的算法就称为页面…

智谱AI多项成果惊艳亮相2021北京智源大会

来源&#xff1a;学术头条2021 年 6 月 1 日&#xff0c;由北京智源人工智能研究院&#xff08;以下简称 “智源”&#xff09;主办的 2021 北京智源大会在北京中关村国家自主创新示范区会议中心成功开幕。包括 Yoshua Bengio、David Patterson 等图灵奖获得者在内的两百余位国…

python-字典方法

1.字典的格式化字符串在转换说明符%后面加上键(圆括号括起来)&#xff1b;phonebook{"A":45,"B":"56"} s"As phone number is %(A)s." print(s%phonebook) 运行结果&#xff1a; #字典方法&#xff1a; #clear():清楚字典中的所有的项…

获得诺贝尔奖的底层小职员 | 从来没有一个高手,是在一夜之间强大起来的

来源&#xff1a;Pinterest优选2019年初NHK的一个访谈纪录片&#xff0c;看哭了很多网友。“感动&#xff0c;这才是真正的大神啊&#xff01;”纪录片的主人公&#xff0c;是2002年的诺贝尔化学奖得主——田中耕一。十九年前&#xff0c;他的获奖几乎是“都市传说”般的爆炸新…

光辉岁月:人工智能的那些人和事(1)

来源&#xff1a;图灵人工智能源头茫昧虽难觅&#xff0c;活水奔流喜不休。——法国数学家亨利庞加莱&#xff08;Henri Poincare&#xff09;目前&#xff0c;人工智能&#xff08;Artificial Intelligence&#xff0c;AI&#xff09;正在迅速崛起。现已面世的AI应用&#xff…

MySQL主从架构及读写分离实战

​​​​​​ 目录 一、实验目的与环境 二、基础环境介绍 三、搭建主从集群 1、理论基础 2、同步的原理 3、搭建主从集群 3.1 配置master主服务器 3.2 配置slave从服务 3.3 主从集群测试 3.4 集群搭建扩展&#xff1a; 3.5、GTID同步集群 4、集群扩容 5、半同步复…

编译原理课程实践——实现一个初等函数运算语言的解释器或编译器

编译原理课程实践——实现具有初等函数运算语言的解释器或编译器 作者&#xff1a;Sylvan Ding &#xff5c;转载请注明文章出处&#xff01; 摘要&#xff1a;本文主要内容是设计词法分析器、语法分析器&#xff08;LL(1)、SLR(1)&#xff09;和语义分析器&#xff08;基于SL…

数据结构开发(3):线性表的顺序存储结构

0.目录 1.线性表的本质和操作 2.线性表的顺序存储结构 3.顺序存储结构的抽象实现和具体实现 3.1 SeqList3.2 StaticList 和 DynamicList4.顺序存储线性表的分析 4.1 效率分析4.2 功能分析5.小结 1.线性表的本质和操作 线性表 ( List ) 的表现形式&#xff1a; 零个或多个数据元…

小目标检测的一些问题,思路和方案

来源&#xff1a;机器学习研究组订阅机器学习正越来越多地进入我们的日常生活。从个人服务的广告和电影推荐&#xff0c;到自动驾驶汽车和自动送餐服务。几乎所有的现代自动化机器都能“看”世界&#xff0c;但跟我们不一样。为了像我们人类一样看到和识别每个物体&#xff0c;…

python-条件语句

#条件、循环和其他语句 #print:可以打印多个表达式&#xff0c;表达式之间用逗号隔开 print(a,"b",False)#参数并不构成一个元组 模块导入:import x :导入模块xfrom x import func &#xff1a;导入模块x的函数funcfrom x import func1,func2,... 导入模块…

一文读懂全球半导体市场

来源&#xff1a;深城物联作者&#xff1a;孙卓异&#xff0c;供职于赛迪顾问集成电路产业研究中心 半导体是当今信息技术产业高速发展的基础和原动力&#xff0c;已经高度渗透并融合到了经济、社会发展的各个领域&#xff0c;其技术水平和发展规模已经成为衡量一个国家产业竞争…

如何写好一份技术简历?

写简历的基本目的和策略 大部分情况下&#xff0c;写简历是找工作的第一步&#xff0c;考虑到第二步就是面试&#xff0c;那么简历就是敲门砖&#xff0c;为了让企业认识到你的价值&#xff0c;必须把自己的真实水平描述出来&#xff0c;展现出你有能力应对这份工作。甚至要体现…