1. 概念
信号量可理解为进化版的互斥锁/量,允许多个线程访问共享资源。由于互斥锁的力度比较大,如果希望在多个线程间对某一对象的部分数据进行共享,使用互斥锁是没有办法实现的,只能将整个数据对象锁住。这样虽然达到了多线程操作共享数据时保证数据正确的目的,却无形中导致线程的并发性下降。线程从并行执行变成了串行执行,与直接使用单进程无异。信号量,是相对折中的一种处理方式,既能保证同步,数据不混乱,又能提高线程并发。
2.主要应用函数
- sem_init函数
- sem_destroy函数
- sem_wait函数
- sem_trywait函数
- sem_timedwait函数
- sem_post函数
以上6个函数的返回值都是:成功返回0,失败返回-1,同时设置errno。(注意它们没有pthread前缀);
sem_t类型,本质仍然是结构体,但应用期间可简单看做为整数,忽略实现细节(类似于使用文件描述符);
sem_t sem;规定信号量sem不能<0,头文件#include<semaphore.h>。
3.信号量基本操作
sem_wait: (1)信号量大于0,则信号量--自减,类比pthread_mutex_lock;(2)信号量等于0,造成线程阻塞。
|
对应
|
sem_post: 信号量++自加,同时唤醒阻塞在信号量上的线程,类比pthread_mutex_unlock。
但,由于sem_t的实现对用户隐藏,所以所谓的++、--操作只能通过函数实现,而不能直接++、--符号运算。信号量的初值,决定了占用信号量的线程的个数。
4.具体函数
(1)sem_init函数
int sem_init(sem_t *sem, int pshared, unsigned int value);
作用:初始化一个信号量
参数:sem, 信号量;pshared,取0用于线程间,取非0(一般为1)用于进程间;value, 指定信号量的初值。
(2)sem_destroy函数
int sem_destroy(sem_t *sem);
作用:销毁一个信号量
(3)sem_wait函数
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
作用:给信号量加锁、尝试加锁,--。申请信号量,申请成功,value--。
sem_timedwait()与sem_wait()相同,不同之处在于abs_timeout指定了在无法立即执行减量操作时调用应阻塞的时间限制。 abs_timeout参数指向一个结构,该结构指定自1970年1月1日00:00:00 +0000(UTC)以来的绝对超时(以秒和纳秒为单位)。
此结构定义如下:
struct timespec {
time_t tv_sec; / *秒* /
long tv_nsec; / *纳秒[0 .. 999999999] * /
};
如果超时已在调用时到期,并且信号无法立即锁定,则sem_timedwait()会失败,并显示超时错误(将errno设置为ETIMEDOUT)。
如果可以立即执行该操作,则无论abs_timeout的值如何,sem_timedwait()都不会因超时错误而失败。 此外,在这种情况下不检查abs_timeout的有效性。
(4)sem_post函数
int sem_post(sem_t *sem);
作用:给信号量解锁,释放信号量value++。
5.实例--生产者与消费者模型
/*生产者与消费者模型,采用信号量'维持'正常有序运转*/
#include<stdio.h>
#include<unistd.h>
#include<semaphore.h>
#include<pthread.h>
#include<stdlib.h>
sem_t blank, xfull;
#define _SEM_CNT_ 5 //指定信号量的初值,定义一个全局变量表示
int queue[_SEM_CNT_]; //模拟生产者存放产品的“仓库”,生产者每生产1个产品,仓库空位blank减去1,消费者每从仓库消费1个产品,仓库空位置腾出来1个blank+1。
int beginnum = 100; //产品编号从100开始计数
void *thr_producter(void *arg)
{
int i=0;
while (1)
{
sem_wait(&blank); //申请仓库中1个空位置资源,blank--
printf("-------%s----self---%lu---num---%d\n",__FUNCTION__,pthread_self(),beginnum);
queue[(i++)%_SEM_CNT_] = beginnum++; //生产1个产品
sem_post(&xfull); //xfull++,仓库占用位置+1
sleep(rand()%3);
}
return NULL;
}
void *thr_customer(void *arg)
{
int i=0;
int num=0;
while (1)
{
sem_wait(&xfull); //消费者等待消费,仓库仓库占用位置腾出1个,xfull--
num = queue[(i++)%_SEM_CNT_]; //消费
printf("-------%s----self---%lu---num---%d\n",__FUNCTION__,pthread_self(),num);
sem_post(&blank); //消费完腾出一个空位置blank++给生产者继续生产
sleep(rand()%3);
}
return NULL;
}
int main()
{
sem_init(&blank,0,_SEM_CNT_); //初始化信号量,生产者
sem_init(&xfull,0,0); //消费者一开始的初始化默认没有产品,也就是信号量初值value为0
//创建线程
pthread_t tid[2];
pthread_create(&tid[0],NULL,thr_producter,NULL);
pthread_create(&tid[1],NULL,thr_customer,NULL);
//线程分离
pthread_join(&tid[0],NULL);
pthread_join(&tid[1],NULL);
//销毁信号量
sem_destroy(&blank);
sem_destroy(&xfull);
return 0;
}
输出:
~$ gcc sem_product.c -lpthread
~$ ./a.out
-------thr_producter----self---140089735280384---num---100
-------thr_customer----self---140089726887680---num---100
-------thr_producter----self---140089735280384---num---101
-------thr_customer----self---140089726887680---num---101
-------thr_producter----self---140089735280384---num---102
-------thr_customer----self---140089726887680---num---102
-------thr_producter----self---140089735280384---num---103
-------thr_customer----self---140089726887680---num---103
-------thr_producter----self---140089735280384---num---104
-------thr_customer----self---140089726887680---num---104
-------thr_producter----self---140089735280384---num---105
-------thr_customer----self---140089726887680---num---105
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)