进化版的互斥锁(1 --> N)
由于互斥锁的粒度比较大,如果我们希望在多个线程间对某一对象的部分数据进行共享,使用互斥锁是没有办法实现的,只能将整个数据对象锁住。这样虽然达到了多线程操作共享数据时保证数据正确性的目的,却无形中导致线程的并发性下降。线程从并行执行,变成了串行执行。与直接使用单进程无异。
信号量,是相对折中的一种处理方式,既能保证同步,数据不混乱,又能提高线程并发。
sem_wait:
1.信号量大于0,则信号量-- (类比pthread_mutex_lock)
2.信号量等于0,造成线程阻塞
sem_post:
将信号量++,同时唤醒阻塞在信号量上的线程 (类比pthread_mutex_unlock)
但,由于sem_t的实现对用户隐藏,所以所谓的++、–操作只能通过函数来实现,而不能直接++、–符号。
信号量的初值,决定了占用信号量的线程的个数。
初始化一个信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);
参1:sem信号量
参2:pshared取0用于线程间;取非0(一般为1)用于进程间
参3:value指定信号量初值
销毁一个信号量
int sem_destroy(sem_t *sem);
P操作,给信号量加锁 –
1.信号量大于0,则信号量-- (类比pthread_mutex_lock)
2.信号量等于0,造成线程阻塞
int sem_wait(sem_t *sem);
V操作,给信号量解锁 ++
int sem_post(sem_t *sem);
尝试对信号量加锁 – (与sem_wait的区别类比lock和trylock)
即使信号量为0也不会阻塞。
int sem_trywait(sem_t *sem);
限时尝试对信号量加锁 –
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
参2:abs_timeout采用的是绝对时间。
定时1秒:
time_t cur = time(NULL);//获取当前时间。
struct timespec t;//定义timespec 结构体变量t
t.tv_sec = cur+1;//定时1秒
t.tv_nsec =0;
sem_timedwait(&sem, &t);//传参
例1:信号量实现“生产者-消费者”模型(使用两个信号量)
/*信号量实现 生产者 消费者问题*/
#include
#include
#include
#include
#include
#define NUM 5
int queue[NUM]; //全局数组实现环形队列
sem_t blank_number, product_number; //空格数信号量, 产品数信号量
void *producer(void *arg)
{
int i = 0;
while (1)
{
sem_wait(&blank_number); //生产者将空格子数--,为0则阻塞等待
int data=rand() % 1000 + 1;
queue[i] = data; //生产一个产品
i = (i+1) % NUM; //借助下标实现环形
sem_post(&product_number); //将产品数++
printf("----Produce---%d\n",data);
sleep(rand()%3);
}
}
void *consumer(void *arg)
{
int i = 0;
while (1)
{
sem_wait(&product_number); //消费者将产品数--,为0则阻塞等待
int data=queue[i];
queue[i] = 0; //消费一个产品
i = (i+1) % NUM;
sem_post(&blank_number); //消费掉以后,将空格子数++
printf("-Consume---%d\n", data);
sleep(rand()%3);
}
}
int main(int argc, char *argv[])
{
pthread_t pid, cid;
sem_init(&blank_number, 0, NUM); //初始化空格子信号量为5
sem_init(&product_number, 0, 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;
}
例2:信号量实现“多生产者-多消费者”模型
多生产者-多消费者模型的重点在于在信号量的基础上再加上一个互斥锁,保证一次只能有一个线程在生产或消费。
/*信号量实现 多生产者 多消费者问题*/
#include
#include
#include
#include
#include
#define NUM 5
int queue[NUM]; //全局数组实现环形队列
sem_t blank_number, product_number; //空格数信号量, 产品数信号量
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
int prodeucer_i = 0;
int consumer_i = 0;
void *producer(void *arg)
{
while (1)
{
sem_wait(&blank_number); //生产者将空格子数--,为0则阻塞等待
pthread_mutex_lock(&mutex1);
int data=rand() % 1000 + 1;
queue[prodeucer_i] = data; //生产一个产品
prodeucer_i = (prodeucer_i+1) % NUM; //借助下标实现环形
pthread_mutex_unlock(&mutex1);
sem_post(&product_number); //将产品数++
printf("----Produce%d---%d\n",(int)arg,data);
sleep(rand()%3);
}
}
void *consumer(void *arg)
{
while (1)
{
sem_wait(&product_number); //消费者将产品数--,为0则阻塞等待
pthread_mutex_lock(&mutex2);
int data=queue[consumer_i];
queue[consumer_i] = 0; //消费一个产品
consumer_i = (consumer_i+1) % NUM;
pthread_mutex_unlock(&mutex2);
sem_post(&blank_number); //消费掉以后,将空格子数++
printf("-Consume%d---%d\n",(int)arg,data);
sleep(rand()%3);
}
}
int main(int argc, char *argv[])
{
pthread_t pid1,pid2,cid1,cid2;
sem_init(&blank_number, 0, NUM); //初始化空格子信号量为5
sem_init(&product_number, 0, 0); //产品数为0
pthread_create(&pid1, NULL, producer, (void*)1);
pthread_create(&pid2, NULL, producer, (void*)2);
pthread_create(&cid1, NULL, consumer, (void*)1);
pthread_create(&cid2, NULL, consumer, (void*)2);
pthread_join(pid1, NULL);
pthread_join(pid2, NULL);
pthread_join(cid1, NULL);
pthread_join(cid2, NULL);
sem_destroy(&blank_number);
sem_destroy(&product_number);
pthread_mutex_destroy(&mutex1);
pthread_mutex_destroy(&mutex2);
return 0;
}