线程同步: 信号量sem_t


1 信号量

信号量是锁,是一种升级的mutex(1->N)
N:信号量在初始化时,可以指定共享资源的数量
信号量和互斥锁的区别:互斥锁只能使得一个线程进入临界区,而信号量可以使得多个线程可以在同一时刻进入临界区(进入临界区线程的个数由初始的资源书决定)

2 信号量API

#include<semaphore.h> //头文件

sem_t sem; //信号量类型

int sem_destroy(sem_t *sem); //销毁
int sem_init(sem_t *sem, int pshared, unsigned int value); //初始化信号量

P操作:
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem); //不阻塞,直接返回错误码
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); //[限时]

V操作:int sem_post(sem_t *sem);

int sem_init(sem_t *sem, int pshared, unsigned int value); //初始化信号量
	int pshared:取0-线程同步;取非0-进程同步
	value:(共享资源的最大值)
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); 
参数:abs_timeout使用案例
    time_t cur = time(NULL); 获取当前时间。
    struct timespec t;    定义timespec 结构体变量t
    t.tv_sec = cur+1; 定时1秒
    t.tv_nsec = t.tv_sec +100;
    sem_timedwait(&sem, &t); 传参

示例代码1:[单生产者/单消费者]单链表的插入和删除

sem_t produce;   
sem_t custom;    
       
typedef struct node{       
  int data;      
  struct node* next;       
}LNode;  
LNode* head=NULL;
       
void* producer(void* arg){ 
  while(1){      
    LNode* newnode=(LNode*)malloc(sizeof(LNode));        
    newnode->data=rand()%100;        
    newnode->next=NULL;    
       
    sem_wait(&produce);  // 生产者获得还能生产几个产品,P(produce)
       
    newnode->next=head;  head=newnode; //insert node     
    printf("produce=%d\n",newnode->data);      
       
    sem_post(&custom); //生产者生产完产品后,V(custom)
       
    sleep(rand()%3);       
  }    
  return NULL;   
}  
void* customer(void* arg){ 
  while(1){      
    sem_wait(&custom);  //消费者获得产品,P(custom)
       
    LNode* delnode=head; head=head->next;      
    printf("      custom=%d\n",delnode->data); 
    free(delnode);         
       
    sem_post(&produce); //        
       
    sleep(rand()%3);       
  }    
  return NULL;   
}      
int main(){      
  sem_init(&produce,0,10);  //初始时:最多允许生产10个产品
  sem_init(&custom,0,0); //初始时:不允许消费产品 
       
  pthread_t tid1,tid2;     
  pthread_create(&tid1,NULL,producer,NULL);    
  pthread_create(&tid2,NULL,customer,NULL);    
       
  pthread_join(tid1,NULL); 
  pthread_join(tid2,NULL); 

  sem_destroy(&produce);
  sem_destroy(&custom);

  return 0;
}

示例代码2

程序功能:生产者和消费者数目都为5,产品缓冲为10,生产者每2秒生产一个产品,消费者每5秒消费一个产品,Ctrl+退出程序
代码详解:
1.缓冲区大小为N
2.使用两个信号量控制生产者/消费者的行为:当缓冲区空时,消费者不能再消费产品,处于阻塞状态;当缓冲区满时,生产者不能生产产品,处于阻塞状态
3.使用一个互斥锁控制in/out下标:多个线程之间要对临界资源(下标in/out,都与共享资源buff[M]相关)进行操作,因此必须保证一次只有一个线程操作下标in/out—>因此需要加锁
4.为什么可以打印出线程的id?–>因为线程函数中有自己的私有局部变量id,一旦id被赋值后将不会改变

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
 
#define N 5   // 消费者或者生产者的数目
#define M 10 // 缓冲数目
//int M=10;
int in = 0; // 生产者放置产品的位置
int out = 0; // 消费者取产品的位置
 
int buff[M] = { 0 }; // 缓冲初始化为0,开始时没有产品
 
sem_t empty_sem; // 同步信号量,当满了时阻止生产者放产品
sem_t full_sem; // 同步信号量,当没产品时阻止消费者消费
pthread_mutex_t mutex; // 互斥信号量,一次只有一个线程访问缓冲
 
int product_id = 0; //生产者id
int prochase_id = 0; //消费者id
//信号处理函数
void Handlesignal(int signo){
    printf("程序退出\n",signo);
    exit(0);
}
/* 打印缓冲情况 */
void print() {
       int i;
       printf("产品队列为");
       for(i = 0; i < M; i++)
    printf("%d", buff[i]);
       printf("\n");
}
 
/* 生产者方法 */
void *product() {
       int id = ++product_id;
       while(1) {//重复进行
    //用sleep的数量可以调节生产和消费的速度,便于观察
    sleep(2);
 
    sem_wait(&empty_sem);
    pthread_mutex_lock(&mutex);
 
    in= in % M;
    printf("生产者%d在产品队列中放入第%d个产品\t",id, in);
 
    buff[in]= 1;
    print();
    ++in;
 
    pthread_mutex_unlock(&mutex);
    sem_post(&full_sem);
       }
}
 
/* 消费者方法 */
void *prochase() {
       int id = ++prochase_id;
       while(1) {//重复进行
    //用sleep的数量可以调节生产和消费的速度,便于观察
    sleep(5);
 
    sem_wait(&full_sem);
    pthread_mutex_lock(&mutex);
 
    out= out % M;
    printf("消费者%d从产品队列中取出第%d个产品\t",id, out);
 
    buff[out]= 0;
    print();
    ++out;
 
    pthread_mutex_unlock(&mutex);
    sem_post(&empty_sem);
       }
}
 
int main() {
       printf("生产者和消费者数目都为5,产品缓冲为10,生产者每2秒生产一个产品,消费者每5秒消费一个产品,Ctrl+退出程序\n");
       pthread_t id1[N];
       pthread_t id2[N];
       int i;
       int ret[N];
       //结束程序
    if(signal(SIGINT,Handlesignal)==SIG_ERR){//按ctrl+C产生SIGINT信号
    printf("信号安装出错\n");
    }
// 初始化同步信号量
       int ini1 = sem_init(&empty_sem, 0, M);//产品队列缓冲同步
       int ini2 = sem_init(&full_sem, 0, 0);//线程运行同步
       if(ini1 && ini2 != 0) {
    printf("信号量初始化失败!\n");
    exit(1);
       }
//初始化互斥信号量
       int ini3 = pthread_mutex_init(&mutex, NULL);
       if(ini3 != 0) {
    printf("线程同步初始化失败!\n");
    exit(1);
       }
// 创建N个生产者线程
       for(i = 0; i < N; i++) {
    ret[i]= pthread_create(&id1[i], NULL, product, (void *) (&i));
    if(ret[i] != 0) {
 printf("生产者%d线程创建失败!\n", i);
 exit(1);
    }
       }
//创建N个消费者线程
       for(i = 0; i < N; i++) {
    ret[i]= pthread_create(&id2[i], NULL, prochase, NULL);
    if(ret[i] != 0) {
 printf("消费者%d线程创建失败!\n", i);
 exit(1);
    }
       }
//等待线程销毁
       for(i = 0; i < N; i++) {
    pthread_join(id1[i], NULL);
    pthread_join(id2[i],NULL);
       }
       exit(0);
}

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值