c 语言信号量,C语言中的信号量

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

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

这一章展示了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; }

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值