多线程与信号量

多线程与信号量

我们的目标是快乐地学习计算机!O(∩_∩)O~

 

【信号量】

虽然它叫信号,其实从算法理解上和信号没啥关系。更好的理解是用于进程之间通信“我要运行,你不要运行”的变量。

信号量为0(≤0)表示“别动”,信号量是非0(>0)表示“可以动”

 

【操作】

信号量只有三个操作:

① 赋予一个初始值 (init)

② 判断是不是0,如果是0,挂起,如果是正数,信号量减1 (wait)

③ 信号量加1 (signal)

 

【应用】

信号量有两个基本应用:

互斥锁定:

使用wait(a)和signal(a),中间的进程就会变成锁定运行,其它进程无法干扰,用于同类型的多个并发线程的隔离

举例:两个同学抢一个厕所

init(sig=1) //厕所是空的,可以用
wait(sig)  //判断厕所是否可以用,不能用就等待,能用就用
process #0  //上厕所
signal(sig)  //告诉大家,我已经出来了,厕所空了

同步演化:

使用两个信号量,wait(a)signal(b)和wait(b)signal(a),显然,两者是互相演化的,有#0则#1可以执行,有#1则#0可以执行

举例:一个生产者为一个消费者提供产品(库存空间有限)

init(sig1=1) //"库存现在是空的,可以生产"
init (sig2=0) //"没有产品可以获取"


#0{
    wait(sig1) //判断是否有库存,没有就等待消费者获取,有就生产
    process #0
    signal(sig2) //告诉消费者你可以获取产品了
}

#1{
    wait(sig2) //等待可以获取产品的消息,如果没有就等待,有就获取
    process #1
    signal(sig1) //告诉消费者我已经取走了,库存已经空了
}

 

 

【代码实现】

最简单的wait / signal代码

void wait(){
    while (sig<=0){;}
    sig--;
}

void signal(){
    sig++;
}

 

【实战】

实际应用中,用上述代码是不行的,因为需要进入核心态,关闭中断,否则互斥的问题依然存在

引用头文件:

定义一个信号量的变量:

sem_t  <name>;

注意到我们前面说的,信号量只有三个操作:

①赋予初值:

sem_init()函数,第一个变量为选择的信号量对象,第二个参数0位线程1位进程,第三个参数为初值

②判断是否≤0,如果是就挂起,否则信号量减1:

sem_wait()函数,参数为选择的信号量对象

③信号量加1:

sem_post()函数,参数为选择的信号量对象

下面我们来看一个实例:

有A个生产者给B个消费者供应产品,产品的库存(缓冲池)只有N块,生产者之间互斥竞争放入产品,而消费者互斥竞争取走产品,生产者与消费者之间则同步放入和取走产品。若库存满了,则生产者停工,若库存为空,则消费者等待。每个生产者必须生产15个物品,每个消费者能且只能拿15个物品,如图:

以下代码取A=5,B=5,N=8:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#define N  8
#define PRODUCT_NUM 15

struct good{
	int id;
	int producer;
};
//The origin code gives an algorithm to solve synchronization
//Now we need to solve asynchronization

struct good buffer[N+1];
int readpos = 1, writepos = 1;
sem_t empty, full;
sem_t p_lock,c_lock;        

void *produce(void * id){
  int i;
  for (i = 0; i < PRODUCT_NUM; i++){
    sem_wait(&empty);
		sem_wait(&p_lock);			 
    buffer[writepos].id = i + 1;
		buffer[writepos].producer = *(int*)id;
	  printf("producer %d create %d-%d At: %d\n",*(int*)id ,*(int*)id, i+1,writepos);   
		writepos++;
    if (writepos > N)
      writepos = 1;
		sem_post(&p_lock);			 
    sem_post(&full);
  }
}

void *consume(void * id){
  int i;
  for (i = 0; i < PRODUCT_NUM; i++){
    sem_wait(&full);
		sem_wait(&c_lock);			 
    printf("consumer %d get %d-%d At: %d\n",*(int*)id , buffer[readpos].producer,buffer[readpos].id,readpos);   
    buffer[readpos].id =  - 1;
		buffer[readpos++].producer =  - 1;
    if (readpos > N)
      readpos = 1;
		sem_post(&c_lock);			 
    sem_post(&empty);
  }
}

int main(){
  int res1,res2,res3,res4,res5,res6,res7,res8,res9, i;               
	int id[6]={0,1,2,3,4,5};   

  pthread_t p1,p2,p3,p4,p5;
	pthread_t c1,c2,c3,c4;  //c5 is the main thread

  for (i = 0; i < N; i++)
  buffer[i].producer =  - 1;
	buffer[i].id =  - 1;
  sem_init(&full, 0, 0);
  sem_init(&empty, 0, N);
	sem_init(&p_lock,0,1);           
	sem_init(&c_lock,0,1);           

  res1 = pthread_create(&p1, NULL, produce, &id[1]);
  res2 = pthread_create(&p2, NULL, produce, &id[2]);
  res3 = pthread_create(&p3, NULL, produce, &id[3]);
  res4 = pthread_create(&p4, NULL, produce, &id[4]);
  res5 = pthread_create(&p5, NULL, produce, &id[5]);

  res6 = pthread_create(&c1, NULL, consume, &id[1]);
  res7 = pthread_create(&c2, NULL, consume, &id[2]);
  res8 = pthread_create(&c3, NULL, consume, &id[3]);
  res9 = pthread_create(&c4, NULL, consume, &id[4]);

  if (res1 || res2 || res3 || res4 || res5 || res6 || res7 || res8 || res9 != 0){      
    perror("failed to create thread");
    exit(1);
  }
  consume(&id[5]);   
  return 0;
}

运行结果(每次运行结果不一定一样,此为其中某一次):

 

  • 12
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值