一、信号量
信号量是进化版的互斥锁,由于互斥锁的粒度比较大,如果我们希望在多个线程间对某一对象的部分数据进行共享,使用互斥锁是没有办法实现的,只能将整个数据对象锁住,这样虽然达到了多线程操作共享数据正确性的目的,却无形中导致线程的并发性下降。线程从并行执行,变成了串行执行。与直接使用单进程无异。而信号量是相对折中的一种处理方式,既能保证同步,数据不混乱又能提高线程的并发。
二、主要应用函数
sem_t sem; 定义信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:
sem: 信号量
pshared: 0: 用于线程间同步
1: 用于进程间同步
value:N 值(指定同时访问的线程数)
int sem_destroy(sem_t *sem);
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
一次调用,做一次-- 操作,当信号量的值为 0 时,再次 -- 就会阻塞。
(对比 pthread_mutex_lock())
int sem_post(sem_t *sem);
一次调用,做一次++ 操作
(对比 pthread_mutex_unlock())
返回值: 均是成功:0 失败:-1,errno被设置
三、生产者消费者模型
开发流程图示:
需要注意的是:使用信号量实现生产者消费者模型,对于生产过程和消费过程并不是原子操作,单一生产者、消费者没什么问题,但是多生产者、消费者情况下,生产过程或消费过程过于耗时,不能保证不会出现生产冲突,消费冲突的后果。
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <semaphore.h>
#define MAX 5
sem_t sem_pro; //产品
sem_t sem_sto; //容量
int queue[MAX];
int i, j;
void *producer(void* arg)
{
while (1) {
sem_wait(&sem_sto); //容量--,容量为0则阻塞等待
queue[j] = rand()%200 + 1; //生产产品
printf("%lu:Produce a product:%d\n", pthread_self(), queue[j]);
sem_post(&sem_pro); //产品数++
j = (j+1) % MAX; //借助下标实现环形队列
sleep(rand() % 3);
}
pthread_exit(NULL);
}
void *consumer(void *arg)
{
while (1) {
sem_wait(&sem_pro); //产品--,产品为0则阻塞等待
printf(" %lu:Consume a product:%d\n", pthread_self(), queue[i]);
queue[i] = 0; //消费产品
sem_post(&sem_sto); //容量++
i = (i+1) % MAX; //借助下标实现环形队列
sleep(rand()%3);
}
pthread_exit(NULL);
}
int main()
{
int ret;
pthread_t producerID;
pthread_t consumerID;
srand(time(NULL));
sem_init(&sem_pro, 0, 0);
sem_init(&sem_sto, 0, MAX);
ret = pthread_create(&producerID, NULL, producer, NULL);
if (ret != 0) {
fprintf(stderr, "pthread_create:producer error ret:%s\n", strerror(ret));
exit(-1);
}
ret = pthread_create(&consumerID, NULL, consumer, NULL);
if (ret != 0) {
fprintf(stderr, "pthread_create:consumer error ret:%s\n", strerror(ret));
exit(-1);
}
pthread_join(producerID, NULL);
pthread_join(consumerID, NULL);
sem_destroy(&sem_pro);
sem_destroy(&sem_sto);
return 0;
}
运行结果:
140011622741760:Produce a product:164
140011614349056:Consume a product:164
140011622741760:Produce a product:10
140011614349056:Consume a product:10
140011622741760:Produce a product:137
140011622741760:Produce a product:167
140011622741760:Produce a product:118
140011614349056:Consume a product:137
140011614349056:Consume a product:167
140011622741760:Produce a product:43
140011614349056:Consume a product:118
140011614349056:Consume a product:43
140011622741760:Produce a product:21
140011614349056:Consume a product:21