Linux基础——线程同步之信号量

1、信号量概念

  • 进化版本的互斥锁,在互斥锁中,我们用的是两个线程,而这个信号量是可以N个线程。

  • 本质是一个计数器

    • 信号量大于0,则信号量- -
    • 信号量等于0,造成阻塞
  • 既可以在线程中使用,也可以在进程中使用

  • 他和管道不同在于,他主要是保护数据,而不是传输数据,他是为了在某一个时刻只有一个线程(或进程)来对数据进行操作
    注意:信号量和信号是两个不同的关系,要做一个区分。

2、信号量原语

sem_t 建立信号量
sem_init 初始化信号量
sem_wait 阻塞
sem_trywait 尝试阻塞
sem_timedwait 计时阻塞
sem_post 唤醒信号量
sem_destroy 销毁信号量

  • 头文件:#include <semaphore.h>
  • 返回值:都是成功为0,失败位-1

2.1、创建信号量

sem_t类型,用来创建信号量

  • 示例:sem_t blank_number这样就创建了一个 名为 blank_number的信号量

2.2、初始化信号量

  • 函数:int sem_init(sem_t *sem, int pshared, unsigned int value);
  • 参数解析:
    • 参数1:传入一个信号量结构体进行初始化
    • 参数2:此参数如果为0表示当前进程的局部信号量,否则,其他进程就能够共享这个信号量(说白了就是,如果为0用于线程,不为零用于进程)
    • 参数3:赋值给信号量对象的一个初始值,这个初始值决定作用信号量的线程(进程)个数
  • 作用:对给定信号量对象进行初始化。

2.3、信号量唤醒

  • 函数:int sem_post(sem_t *sem);
  • 参数解析:传入一个信号量结构体进行唤醒
  • 作用:将信号量++,同时唤醒阻塞在信号量的线程,相当于unlock

2.4、信号量阻塞

2.4.1、普通阻塞

函数:int sem_wait(sem_t *sem);
参数解析:传入一个信号量结构体,来改变对象的值
作用:把信号量- -,如果信号量的值大于零,那么解析继续进行,函数立即返回。 如果信号量当前的值为零,那么调用就会阻塞,直到可以执行减量操作(例如,信号量值上升到零以上) ,或者信号中断调用

2.4.2、尝试阻塞

函数:int sem_trywait(sem_t *sem);
参数解析:传入一个信号量结构体,来改变对象的值
作用:与 sem_wait ()相同,只是如果不能立即执行递减操作,则调用返回一个错误(errno set to eagain) ,而不是阻塞。

2.4.2、计时阻塞

  • 函数:int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
  • 参数解析:
    • 参数1:传入一个信号量结构体,来改变对象的值
    • 参数2:传入 在一个timespec结构体来设定等待是时间
  • 作用:sem wait ()相同,只是 abs_timeout指定了一个限制,如果不能立即按形式减少调用,则调用应该阻塞的时间量,超过时间返回错误。
  • 时间结构体
    struct timespec
    {
    time_t tv_sec; /* 秒*/
    long tv_nsec; /* 纳秒[0 … 999999999] */
    };

示例代码——生产者消费者模型

#include <stdlib.h> 
#include <pthread.h> 
#include <stdio.h> 
#include <semaphore.h>
//创建一个可容纳5个物体的空间
#define NUM 5 
int queue[NUM]; 
//创建两个信号变量消费者和生产者
//可以理解成blank_number为剩余空间
//product_number为已占用的空间
sem_t blank_number, product_number;

//生产者创建物体
void *producer(void *arg) 
{ 
	int p = 0; 
	while (1) 
	{ 
		//对于生产者来说,生产一个物品,剩余空间blank_number要减少,已占用空间product_number要增加
		//剩余空间blank_number减少
		sem_wait(&blank_number); 
		queue[p] = rand() % 1000 + 1; 
		printf("Produce %d\n", queue[p]); 
		//占用空间product_number增加
		sem_post(&product_number);
		//p取模的话就能把p限制在1-5一直循环 
		p = (p+1)%NUM; 
		//随机睡眠
		sleep(rand()%5); 
	} 
}

//消费者消费物体
void *consumer(void *arg) 
{ 
	//对于消费者来说,剩余空间要增加,占用空间要减少,因为我这个消费者消费了一个物品
	int c = 0; 
	while (1) 
	{ 
		//已占用空间product_number减少
		sem_wait(&product_number); 
		printf("Consume %d\n", queue[c]); 
		queue[c] = 0; 
		//剩余空间blank_number增加
		sem_post(&blank_number); 
		c = (c+1)%NUM; 
		sleep(rand()%5); 
	} 
}
int main(void) 
{ 
	pthread_t pid, cid;
	//初始化两个信号量
	sem_init(&blank_number, 0, NUM); 
	sem_init(&product_number, 0, 0); 
	
	//线程创建
	pthread_create(&pid, NULL, producer, NULL); 
	pthread_create(&cid, NULL, consumer, NULL);
	//线程回收
	pthread_join(pid, NULL); 
	pthread_join(cid, NULL); 
	//信号量销毁
	sem_destroy(&blank_number); 
	sem_destroy(&product_number); return 0;
}

猜你喜欢

转载自blog.csdn.net/l1206715877/article/details/106878104