信号量及其应用详解

本文深入探讨了信号量的概念,包括其引入原因、PV操作的原理,以及在二值信号量、条件变量、生产者消费者模型、读者写者模型和哲学家就餐问题中的应用。此外,还对比了System V和POSIX信号量的区别,强调了信号量在多线程同步中的重要作用。
摘要由CSDN通过智能技术生成

信号量

1、信号量的引入
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>

// global shared varible
volatile int cnt = 0; // counter

// thread routine
void *thread(void* arg)
{
   
  int i, niters = *((int*)arg);
  
  for(int i = 0; i < niters; ++i)
    cnt++;
  
  return NULL;
}

int main(int argc, char* argv[])
{
   
  int niters;
  pthread_t tid1, tid2;

  if(argc != 2)
  {
   
    printf("usage: %s <niters>\n", argv[0]);
    exit(0);
  }

  niters = atoi(argv[1]);
  
  // create thread and wait for them to finish
  pthread_create(&tid1, NULL, thread, &niters);
  pthread_create(&tid2, NULL, thread, &niters);
  pthread_join(tid1, NULL);
  pthread_join(tid2, NULL);

  printf("cnt = %d\n", cnt);

  return 0;
}

当输入的 niters 足够大的时候,发现 cnt 并不等于 2 * niters:

[root@SuperhandsomeChuan synchronization]# ./a.out 100000000
cnt = 127028782
[root@SuperhandsomeChuan synchronization]# ./a.out 200000000
cnt = 255307600

问题就处在,我们无法预测线程的执行顺序

2、PV操作

信号量 s 是具有 非负整数值的全局变量 ,只能由两种特殊的操作来处理,这两种操作称为 РV :

  • P(s):如果 s 是非零的,那么 P 将 s 减 1,并且立即返回。如果 s 为零,那么就挂起这个线程,直到 s 变为非零,而一个 V 操作会重启这个线程。在重启之后, Р 操作将 s 减 1,并将控制返回给调用者。
  • V(s): V 操作将 s 加 1。如果有任何线程阻塞在 Р 操作等待s变成非零,那么 V 操作会重启这些线程中的一个,然后该线程将 s 减 1,完成它的 Р 操作。

P 中的 测试和减 1 操作是不可分割的,也就是说,一旦预测信号量 s 变为非负,就会将 s 减 1,不能有中断。V 中的 加 1 操作和测试 也是不可分割的,也就是 加载、加 1 和存储信号量 的过程中没有中断。注意,V 的定义中没有定义等待线程被重新启动的顺序。唯一的要求是 V 必须 只能重启一个 正在等待的线程。因此,当有多个线程在等待同一个信号量时,你不能预测 V 操作要重启哪一个线程

理解了 PV 操作,我们就可以修改 1 中的程序了。

首先了解一下 POSIX semaphore 接口:

#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_wait(sem_t *sem);
int sem_post(sem_t *sem);

我们来封装一下 PV 操作:

void P(sem_t* s) {sem_wait(s);}
void V(sem_t* s) {sem_post(s);}

修改后的程序:

#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<semaphore.h>

// global shared varible
volatile int cnt = 0; // counter

sem_t mutex;

void P(sem_t* s) {
   sem_wait(s);}
void V(sem_t* s) {
   sem_post(s);}

// thread routine
void *thread(void* arg)
{
   
  int i, niters = *((int*)arg);
  
  for(int i = 0; i < niters; ++i)
  {
   
    P(&mutex);
    cnt++
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值