Linux 信号量 生产者消费者小例题

  菜鸟偶遇信号量,擦出火花(只有不熟才会有火花)。于是上网搜资料和看《Unix环境高级编程》实现了几个小例题,高手请勿喷!这几位写得非常好啊:

题目来源: http://www.it165.net/os/html/201312/7039.html

信号量及其用法:http://www.cnblogs.com/hjslovewcl/archive/2011/03/03/2314341.html

Mutex与Semaphore区别著名的厕所理论:http://koti.mbnet.fi/niclasw/MutexSemaphore.html

   哎呀,暴露了!我不是故意偷窥别人的……




 

一:一个生产者、一个消费者、一个资源情况

  这种情况情况可以只用一个信号量,要生成或要消费只用尝试获取这个信号量,这里用了两个:full=1和empty=0,两个只为了和后面一致,1、0是赋初值。生产者和消费者情况如下:

//生产者:
P(empty)生成资源并放进资源处
V(full)//消费者:
P(full)消费资源
V(empty)

  若生产者最先开始生产资源,P(empty),full和empty都成了0,此时若消费者想要消费,则P(full)时,full为0则睡眠等待,等生产者生结束就把full加1,看到消费者可怜地睡着了就唤醒它,然后消费者把full减1自己快活去了。

  消费者消费过程中生产者若要生了,则会因为empty为0而休眠,等消费者结束就把empty加1,然后生产者开始生产。

  上面的好理解,下面上代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>#include <x86_64-linux-gnu/sys/types.h>
#include <x86_64-linux-gnu/sys/ipc.h>
#include <x86_64-linux-gnu/sys/sem.h>int semInite(int semId, int value);
int semDelete(int semId);
int semP(int semId);
int semV(int semId);//declare a union to be used
union semun 
{int val;                        /* value for SETVAL */ struct semid_ds *buf;                /* buffer for IPC_STAT, IPC_SET */ unsigned short int *array;         /* array for GETALL, SETALL */ struct seminfo *__buf;                /* buffer for IPC_INFO */ 
};//semaphore declare
static int semFullId;
static int semEmptyId;
static int source = 0;        //source definition    //new thread as a consumer
void* child_thread(void* arg)
{int ttt = 1;while(1){sleep(rand() % 19);printf("child No.%d times wants to consume...\n", ttt);semP(semFullId);    //
        printf("child No.%d times start consuming. source = %d\n", ttt, source);source = 0;printf("child No.%d times end consuming. source = %d\n\n", ttt++, source);semV(semEmptyId);    //
    }return (void*)0;
}int main(void)
{    //create semaphoresemFullId = semget((key_t)1235, 1, 0666 | IPC_CREAT);semEmptyId = semget((key_t)1236, 1, 0666 | IPC_CREAT);semInite(semFullId, 0);semInite(semEmptyId, 1);pthread_t pid;pthread_create(&pid, NULL, child_thread, NULL);int tt = 1;    while(1){sleep(rand() % 18);printf("parent No.%d times wants to produce...\n", tt);semP(semEmptyId);    //
        printf("parent No.%d times start producing. source = %d\n", tt, source);source = rand() % 100;printf("parent No.%d times end producing. source = %d\n", tt++, source);semV(semFullId);    //
    }semDelete(semFullId);semDelete(semEmptyId);return 0;
}//set semaphore as default value
int semInite(int semId, int value)
{union semun semUnion;semUnion.val = value;    //set default semaphorereturn semctl(semId, 0, SETVAL, semUnion);
}//delete semaphore
int semDelete(int semId)
{union semun semUnion;return semctl(semId, 0, IPC_RMID, semUnion);
}//semaphore P operation
int semP(int semId)
{struct sembuf semBuf;semBuf.sem_num = 0;        //indicate it is not semaphore arraysemBuf.sem_op = -1;        //subtract onesemBuf.sem_flg = SEM_UNDO;return semop(semId, &semBuf, 1);    //return value
}//semaphore V operation
int semV(int semId)
{struct sembuf semBuf;semBuf.sem_num = 0;        //indicate it is not semaphore arraysemBuf.sem_op = 1;        //subtract onesemBuf.sem_flg = SEM_UNDO;return semop(semId, &semBuf, 1);    //return value
}
111

两个信号量其实应该用信号量集的,因为它本来就是针对集合的,但是由于刚入门,为了易懂,就用两个。两个线程,创建的新线程当做消费者了。其中unix的几个信号量的函数看了半天,有点复杂,简单不准确来讲:

//获得一个信号量啦,第二个参数是想要创建的信号量个数,
//因为unix操作的是信号量集合,设为1不就一个信号量了嘛
//其他参数我不管了
int semget(key_t key, int num_sems, int sem_flags);//信号量集合的操作,这个可以用来实现P、V的 +1 -1 的功能
int semop(int sem_id, struct sembuf *sem_ops, size_t num_sem_ops);//信号量集合的控制,如初始化删除等
int semctl(int sem_id, int sem_num, int command, ...);

运行:




 

二:一个生产者、一个消费者、N个资源情况

  这里资源用是一个数组代替了。其实本质上和上面类似,每次只让生产者或消费者中的一个进入,进入后放到哪个地方或从哪个地方取就得用一个标志来说明了,其实也可以为每一资源加上信号量的。

  这里在生产者和消费者那里都设置了一个static的变量当做游标,指示下个资源放到哪个位置和下次从哪取资源。staitic变量用在这里很合适,因为只会初始化一次。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>#include <x86_64-linux-gnu/sys/types.h>
#include <x86_64-linux-gnu/sys/ipc.h>
#include <x86_64-linux-gnu/sys/sem.h>#define N 5int semInite(int semId, int value);
int semDelete(int semId);
int semP(int semId);
int semV(int semId);//declare a union to be used
union semun 
{int val;                        /* value for SETVAL */ struct semid_ds *buf;                /* buffer for IPC_STAT, IPC_SET */ unsigned short int *array;         /* array for GETALL, SETALL */ struct seminfo *__buf;                /* buffer for IPC_INFO */ 
};//semaphore declare
static int semFullId;
static int semEmptyId;
static int srcArr[N];        //source definition    //new thread as a consumer
void* child_thread(void* arg)
{int ttt = 1;while(1){static int pToGet = 0;    //get source from the positionsleep(rand() % 19);printf("child No.%d times wants to consume(get from index %d)...\n", ttt, pToGet);semP(semFullId);    //
        printf("child No.%d times start consuming.(get from index %d, data is %d)\n", ttt, pToGet, srcArr[pToGet]);srcArr[pToGet] = 0;printf("child No.%d times end consuming. (get from index %d)\n\n", ttt++, pToGet);pToGet = (pToGet + 1) % N;semV(semEmptyId);    //
    }return (void*)0;
}int main(void)
{    //create semaphoresemFullId = semget((key_t)1235, 1, 0666 | IPC_CREAT);semEmptyId = semget((key_t)1236, 1, 0666 | IPC_CREAT);semInite(semFullId, 0);semInite(semEmptyId, N);    //N source
    pthread_t pid;pthread_create(&pid, NULL, child_thread, NULL);int tt = 1;    while(1){static int pToPut = 0;        //next position where source to be filled insleep(rand() % 18);printf("parent No.%d times wants to produce(put in %d index)...\n", tt, pToPut);semP(semEmptyId);    //
        printf("parent No.%d times start producing.(put in %d index, original data is %d)\n", tt, pToPut, srcArr[pToPut]);int temp = rand() % 100;srcArr[pToPut] = temp;printf("parent No.%d times end producing.(put in %d index, now data is %d)\n", tt++, pToPut, srcArr[pToPut]);pToPut = (pToPut + 1) % N;semV(semFullId);    //
    }semDelete(semFullId);semDelete(semEmptyId);return 0;
}//set semaphore as default value
int semInite(int semId, int value)
{union semun semUnion;semUnion.val = value;    //set default semaphorereturn semctl(semId, 0, SETVAL, semUnion);
}//delete semaphore
int semDelete(int semId)
{union semun semUnion;return semctl(semId, 0, IPC_RMID, semUnion);
}//semaphore P operation
int semP(int semId)
{struct sembuf semBuf;semBuf.sem_num = 0;        //indicate it is not semaphore arraysemBuf.sem_op = -1;        //subtract onesemBuf.sem_flg = SEM_UNDO;return semop(semId, &semBuf, 1);    //return value
}//semaphore V operation
int semV(int semId)
{struct sembuf semBuf;semBuf.sem_num = 0;        //indicate it is not semaphore arraysemBuf.sem_op = 1;        //subtract onesemBuf.sem_flg = SEM_UNDO;return semop(semId, &semBuf, 1);    //return value
}
222

运行结果:




 

三:N个生产者,N个消费者,N个资源

   这种情况不仅生产者和消费者之间要通过上述的方式协调使用资源,而且生产者内部和消费者内部也要协调。定义四个信号量:

empty——表示缓冲区是否为空,初值为n。
full——表示缓冲区中是否为满,初值为0。
mutex1——生产者之间的互斥信号量,初值为1。
mutex2——消费者之间的互斥信号量,初值为1。

//生产者进程
P(mutex1)P(empty)生产数据并放进特定位置V(full)
V(mutex1)//消费者进程
P(mutex2)P(full)消费数据V(empty)
V(mutex2)

其实上面生产者或者消费者获取互斥量或信号量的顺序可以颠倒的,不会产生死锁。

  当然这个问题可以用其他更好的方式解决,我还得继续学习。

转载于:https://www.cnblogs.com/jiayith/p/3854312.html

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

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

相关文章

C/C++语言之通过定义指针函数方式来实现在一个cpp文件里面获取另外一个cpp文件函数的返回值

1、定义函数指针 typedef int (* fun) (); static fun f; 2、代码实现 3、结果 4、总结 我们可以这样使用 在a.h文件里面里面定义函数指针,并且有个传递函数指针的方法 typedef std::string (*fun)();void f2(fun f 1); 然后在a.cpp文件里面实现f2方法 static fun f;…

Dapr 中文社区汇总

Dapr 于 2019 年在微软创建。随着时间的推移&#xff0c;许多社区成员加入该项目并做出贡献&#xff0c;扩展并帮助它在 2021 年 2 月达到了稳定的 1.0 版本。2021年3 月提交给 CNCF&#xff0c;在2021年11月被接受 Dapr 作为 CNCF 的孵化项目。关于CNCF 的每个级别的成熟度要求…

逻辑查询优化

为什么80%的码农都做不了架构师&#xff1f;>>> 1、逻辑查询优化 基于关系代数理论&#xff0c;启发式规则&#xff0c;对查询进行等价重写。 2、查询重写规则 &#xff08;1&#xff09;子查询优化 &#xff08;2&#xff09;视图重写 &#xff08;3&#xff09;等…

CentOS 7 Root用户密码重置 2017-04-02

跨平台系列汇总&#xff1a;http://www.cnblogs.com/dunitian/p/4822808.html#linux 异常处理汇总-服 务 器 http://www.cnblogs.com/dunitian/p/4522983.html 开机的时候按e &#xff08;如果正在使用&#xff0c;你可以输入reboot&#xff0c;然后赶紧按回车键&#xff0c;也…

oracle处理考勤时间,拆分考勤时间段的sql语句

最近一直在用mysql数据库做云项目,有段时间没有接触oracle了,昨天有朋友叫我帮忙用oracle处理一个考勤记录的需求,我在考虑如何尽量精简实现上面花了一些时间。于是把这个实现做个总结。 需求如下: rownum为奇数的为进厂时间&#xff0c;偶数的为离场时间第一个奇数行的时间被第…

实现DDD领域驱动设计: Part 2

原文链接: https://dev.to/salah856/implementing-domain-driven-design-part-ii-2i36实现&#xff1a;构建块这是本系列的重要部分。我们将通过示例介绍和解释一些明确的规则。在实现领域驱动设计时&#xff0c;你可以遵循这些规则并应用到你的解决方案中。示例示例将使用GitH…

王彪20162321 2016-2017-2 《程序设计与数据结构》第5周学习总结

王彪 2016-2017-2 《程序设计与数据结构》第5周学习总结 教材学习内容总结 1.关键概念 1.面向对象程序设计的核心是类的定义&#xff0c;它代表了状态和行为的对象。2.变量的作用域依赖于变量声明的位置&#xff0c;作用域决定在哪里可以使用变量。3.对象应该是封装的&#xff…

c语言指针索引数组,C语言数组指针表示法

指针在处理数组时很有用&#xff0c;我们可以用指针指向已有的数组&#xff0c;也可以从堆上分配内存然后把这块内存当做一个数组使用。数组表示法和指针表示法在某种意义上可以互换。不过&#xff0c;它们并不完全相同&#xff0c;后面的“数组和指针的差别”中会详细说明。单…

C# 使用AggregateException 信息

为了得到所有失败任务的异常信息&#xff0c;可以将 Task.WhenAll 返回的结果写到一个Task 变量中。这个任务会一直等到所有任务都结束。否则&#xff0c;仍然可能错过抛出的异常。上一小节中&#xff0c;catch 语句只检索到第一个任务的异常。不过&#xff0c;现在可以访问外部…

Android之内置和外置sdcard路径显示并且写入数据

1、效果图片 2、部分代码 package com.example.sdcardcheck;import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.Array; import java.lang.…

数据挖掘——数据仓库

虽然存在数据仓库并不是数据挖掘的先决条件&#xff0c;但实际上&#xff0c;若能访问数据仓库&#xff0c;数据挖掘的任务就会变得容易的多。 数据仓库的主要目标是增加决策过程的“情报”和此过程的相关人员的知识。数据仓库对不同的人来说有不同的意义。 数据仓库是一个集成…

OxyPlot 导出图片及 WPF 元素导出为图片的方法

OxyPlot 导出图片及 WPF 元素导出为图片的方法目录OxyPlot 导出图片及 WPF 元素导出为图片的方法一、OxyPlot 自带导出方法二、导出 WPF 界面元素的方法三、通过附加属性来使用独立观察员 2022 年 2 月 26 日最近有个需求&#xff0c;就是将 OxyPlot 图形导出图片。经过尝试&am…

delphi中利用Indy的TIdFtp控件实现FTP协议

2019独角兽企业重金招聘Python工程师标准>>> delphi中利用Indy的TIdFtp控件实现FTP协议版权声明&#xff1a;本文为博主原创文章&#xff0c;未经博主允许不得转载。现在很多应用都需要上传与下载大型文件&#xff0c;通过HTTP方式上传大文件有一定的局限性。幸好FT…

C++之map插入数据相同的key不能覆盖value解决办法

1、问题 C里面,如果map里面插入之前的<key, value>,如果key在map里面有的话&#xff0c;不会覆盖之前的value,一般先判断之前有没有数据&#xff0c;有的话先删除&#xff0c;然后再去添加。 2、代码实现 3、运行结果

【BZOJ】【4145】【AMPPZ2014】The Prices

状压DP/01背包 Orz Gromah 容易发现m的范围很小……只有16&#xff0c;那么就可以状压&#xff0c;用一个二进制数来表示买了的物品的集合。 一种简单直接的想法是&#xff1a;令$f[i][j]$表示前$i$个商店买了状态集合为$j$的商品的最小代价&#xff0c;那么我们转移的时候就需…

WPF 实现人脸检测

WPF开发者QQ群此群已满340500857 &#xff0c;请加新群458041663由于微信群人数太多入群请添加小编微信号yanjinhuawechat 或 W_Feng_aiQ 邀请入群需备注WPF开发者 PS&#xff1a;有更好的方式欢迎推荐。接着上一篇利用已经训练好的数据文件,检测人脸 地址如下&#xff1a;http…

C++之函数的默认值参数说明

1、思考 今天看到C代码的时候&#xff0c;发现文件里面的函数定义和实现都有3个参数&#xff0c;特码调用的时候只有2个参数了&#xff0c;日了狗&#xff0c;java里面好像没有这种方式&#xff0c;后来才发现是默认参数 2、代码实现 3、展示结果 4、总结 注意默认参数需要写…

插头DP

AC HDU1693 不能再简单了的插头DP 1 #include <cstdio>2 #include <fstream>3 #include <iostream>4 5 #include <cstdlib>6 #include <cstring>7 #include <algorithm>8 #include <cmath>9 10 #include <queue>11 #include…

自定义控件详解(四):Paint 画笔路径效果

Paint 画笔 &#xff0c;即用来绘制图形的"笔" 前面我们知道了Paint的一些基本用法&#xff1a; paint.setAntiAlias(true);//抗锯齿功能 paint.setColor(Color.RED); //设置画笔颜色 paint.setStyle(Style.FILL);//设置填充样式 paint.setStrokeWidth(10);//设…

2021 .NET Conf China 主题分享之-轻松玩转.NET大规模版本升级

去年.NET Conf China 技术大会上&#xff0c;我给大家分享了主题《轻松玩转.NET大规模版本升级》&#xff0c;今天把具体分享的内容整理成一篇博客&#xff0c;供大家研究参考学习。一、先说一下技术挑战和业务背景我们公司&#xff1a;特来电新能源股份有限公司&#xff1a;中…