信号量是学习同步的一个好方式,但是它们实际上并没有像互斥体和条件变量一样被广泛使用。

尽管如此,还是有一些同步问题可以用信号量简单解决,产生显然更加合适的解决方案。

这一章展示了C语言用于处理信号量的API,以及我用于使它更加容易使用的代码。而且它展示了一个终极挑战:你能不能使用互斥体和条件变量来实现一个信号量?

这一章的代码在本书仓库的semaphore目录中。 11.1 POSIX信号量

信号量是用于使线程协同工作而不互相影响的数据结构。

POSIX标准规定了信号量的接口,它并不是pthreadpthreadsem_t。这个类型表现为结构体,所以如果你将它赋值给一个变量,你会得到它的内容副本。复制信号量完全是一个坏行为,在POSIX中,它的复制行为是未定义的。幸运的是,包装sem_t使之更安全并易于使用相当容易。我的包装API在sem.hSemaphoresem_t的同义词,但是我认为它更加可读,而且大写的首字母会提醒我将它当做对象并使用指针传递它。这些函数的实现在Semaphore

*make_semaphore(intvalue) { Semaphore *sem = check_malloc(sizeof(Semaphore));

int n = sem_init(sem,0, value); if (n != 0) perror_exit("sem_init failed");

return sem; }make_semaphore接收信号量的初始值作为参数。它为信号量分配空间,将信号量初始化,之后返回指向Semaphore

如果执行成功,sem_init返回0;如果有任何错误,它返回-1。使用包装函数的一个好处就是你可以封装错误检查代码,这会使使用这些函数的代码更加易读。

下面是semaphore_wait的实现:

voidsemaphore_wait(Semaphore *sem){ int n = sem_wait(sem); if (n != 0)

perror_exit("sem_wait failed"); }下面是

我更喜欢把这个这个操作叫做“signal”而不是“post”,虽然它们是一个意思(发射)。

译者注:如果你习惯了互斥体(锁)的操作,也可以改成lock和unlock。互斥体其实就是信号量容量为1时的特殊形态。

下面是一个例子,展示了如何将信号量用作互斥体:

Semaphore *mutex = make_semaphore(1); semaphore_wait(mutex); // protected

code goes here semaphore_signal(mutex);

当你将信号量用作互斥体时,通常需要将它初始化为1,来表示互斥体是未锁的。也就是说,只有一个线程可以通过信号量而不被阻塞。

这里我使用了变量名称mutex来表明信号量被用作互斥体。但是要记住信号量的行为和pthread互斥体不完全相同。

使用这些信号量的包装函数,我们可以编写出生产者-消费者问题的解决方案。这一节的代码在queue_sem.c。

下面是Queue的一个新定义,使用信号量来代替互斥体和条件变量:

typedefstruct { int *array; int length; int next_in; int next_out; Semaphore

*mutex; //-- new Semaphore *items; //-- new Semaphore *spaces; //-- new }

Queue;下面是Queue *make_queue(int length) { Queue *queue = (Queue *)

malloc(sizeof(Queue)); queue->length = length;queue->array = (int *)

malloc(length * sizeof(int)); queue->next_in =0; queue->next_out = 0;

queue->mutex = make_semaphore(1); queue->items = make_semaphore(0);

queue->spaces = make_semaphore(length-1);returnqueue;

}mutex用于确保队列的互斥访问,初始值为1,说明互斥体最开始是未锁的。itemqueue_popspaces是队列中剩余空间的数量,也是可非阻塞执行queue_push的线程数量。最开始的空间数量就是队列的容量下面是voidqueue_push(Queue

*queue, int item){ semaphore_wait(queue->spaces);

semaphore_wait(queue->mutex); queue->array[queue->next_in] = item;

queue->next_in = queue_incr(queue, queue->next_in);

semaphore_signal(queue->mutex); semaphore_signal(queue->items);

}要注意queue_push并不需要调用queue_full,因为信号量跟踪了有多少空间可用,并且在队列满了的时候阻塞住生产者。下面是intqueue_pop(Queue

*queue){ semaphore_wait(queue->items); semaphore_wait(queue->mutex); int

item =queue->array[queue->next_out]; queue->next_out =

queue_incr(queue,queue->next_out); semaphore_signal(queue->mutex);

semaphore_signal(queue->spaces); return item; }

这个解决方案在《The Little Book of Semaphores》中的第四章以伪代码解释。

为了使用本书仓库的代码,你需要编译并运行这个解决方案,你应该执行:

$ make queue_sem $ ./queue_sem

任何可以使用信号量解决的问题也可以使用条件变量和互斥体来解决。一个证明方法就是可以使用条件变量和互斥体来实现信号量。

在你继续之前,你可能想要将其做为一个练习:编写函数,使用条件变量和互斥体实现sem.h中的信号量API。你可以将你的解决方案放到本书仓库的mysem.c和mysem.h中,你会在

mysem_soln.c和mysem_soln.h中找到我的解决方案。

如果你在开始时遇到了麻烦,你可以使用下面来源于我的代码的结构体定义,作为提示:

typedef struct { intvalue, wakeups; Mutex *mutex; Cond *cond; }

Semaphore;value是信号量的值。wakeups记录了挂起信号的数量,也就是说它是已被唤醒但是还没有恢复执行的线程数量。wakeups的原因是确保我们的信号量拥有《The

Little Book of

Semaphores》中描述的性质3。mutex提供了value和wakeups的互斥访问,cond是线程在需要等待信号量时所等待的条件变量。

下面是这个结构体的初始化代码:

Semaphore *make_semaphore(intvalue) { Semaphore *semaphore =

check_malloc(sizeof(Semaphore)); semaphore->value = value;

semaphore->wakeups = 0; semaphore->mutex = make_mutex; semaphore->cond

= make_cond; return semaphore; }

c 语言信号量,C语言中的信号量相关推荐

  1. 哲学家就餐问题C语言实现(涉及多线程、信号量等)

    哲学家就餐问题C语言实现(涉及多线程.信号量等) 1.实验原理 2.实验内容 3.代码部分 3.1 重要代码展示 3.2 源码 3.3 运行结果展示 1.实验原理   由Dijkstra提出并解决的哲 ...

  2. 【C语言】【unix c】信号量集(system v ipc)

    二.信号量集(system v ipc)信号量集就是数组,数组里的每个元素都是信号量的类型1.获取键值ftok(3)2.使用键值获取信号量集的idsemget(2)#include <sys/t ...

  3. linux C语言多线程库pthread中条件变量的正确用法逐步详解

    linux C语言多线程库pthread中条件变量的正确用法: 了解pthread常用多线程API和pthread互斥锁,但是对条件变量完全不知道或者不完全了解的人群. 关于条件变量的典型应用,可以参 ...

  4. log包在Golang语言的标准库中是怎么使用的?

    Golang 语言的标准库中提供了一个简单的 log 日志包,它不仅提供了很多函数,还定义了一个包含很多方法的类型 Logger.但是它也有缺点,比如不支持区分日志级别,不支持日志文件切割等. 01. ...

  5. R语言使用party包中的ctree函数构建条件推理决策树的流程和步骤、条件推理决策树是传统决策树的一个重要变体、条件推理树的分裂是基于显著性测试而不是熵/纯度/同质性度量来选择分裂

    R语言使用party包中的ctree函数构建条件推理决策树的流程和步骤(Conditional inference trees).条件推理决策树是传统决策树的一个重要变体.条件推理树的分裂是基于显著性 ...

  6. R语言使用caret包中的createFolds函数对机器学习数据集进行交叉验证抽样、返回的样本列表长度为k个

    R语言使用caret包中的createFolds函数对机器学习数据集进行交叉验证抽样.返回的样本列表长度为k个 目录

  7. R语言使用caret包中的createMultiFolds函数对机器学习数据集进行交叉验证抽样、返回的样本列表长度为k×times个、times为组内抽样次数

    R语言使用caret包中的createMultiFolds函数对机器学习数据集进行交叉验证抽样.返回的样本列表长度为k×times个.times为组内抽样次数 目录

  8. R语言使用party包中的ctree函数构建条件推理决策树(Conditional inference trees)、使用plot函数可视化训练好的条件推理决策树、条件推理决策树的叶子节点的阴影区域表

    R语言使用party包中的ctree函数构建条件推理决策树(Conditional inference trees).使用plot函数可视化训练好的条件推理决策树.条件推理决策树的叶子节点的阴影区域表 ...

  9. R语言使用zoo包中的rollapply函数计算两个时间序列数据列之间的滚动相关性(Rolling correlations)、例如,计算两种商品销售额之间的3个月的滚动相关性

    R语言时间序列数据滚动相关性分析(Rolling correlations).R语言使用zoo包中的rollapply函数计算两个时间序列数据列之间的滚动相关性(Rolling correlation ...

最新文章

  1. sql 删除最低分数_一份虐你千百遍的SQL语句面试题,请笑纳
  2. Typora中使用Gitee图床
  3. codeforces-148D-Bag of mice-概率DP
  4. 通过DBLINK跨数据库查询,同步创建表结构,插入表数据
  5. 歌词 —— 那些花儿
  6. 砍掉中国90%的科研人员,对科技发展的影响微乎其微,某教授酒后真言!
  7. PHP水果店管理系统,赢通水果店管理系统A3专业版
  8. oeasy教您玩转linux-010110内容回顾
  9. HotKey学习总结
  10. Python报错解决:local variable ‘xxx‘ referenced before assignment
  11. conda创建虚拟环境方法和pqi使用国内镜像源安装第三方库的方法教程
  12. linux18.04 英文文件夹,在Ubuntu 18.04 LTS中打开文件夹的6种方法
  13. 猜数字游戏,用户充值版(Java)
  14. 爱快 Open v服务端 全局推送路由器命令
  15. Docker的 Overlay/Overlay2 文件系统
  16. 关于队里面最菜的在博客打卡第六天这件事
  17. 堆栈的区别及增长方向
  18. metasploit msfvenom与veil绕过杀毒软件
  19. 达梦客户端工具的使用
  20. 跨公司销售利润中心替代

热门文章

  1. java中的两同两小一大原则
  2. python语言基础(二)环境搭建
  3. 从笔记本的系统规划面试你的另一半
  4. 【设计杂志的软件】名编辑电子杂志大师教程 | 添加本地视频
  5. 【Python】积累与发现
  6. 微信小程序引入公用css及js
  7. chatGPT+AI绘画(bluewillow)
  8. 使用批处理指令强制删除异常文件或文件夹
  9. 让父母小孩都离不开!天猫精灵打造完美智慧生活
  10. 你,远程工作倦怠了吗?