说到信号量,我们立即反应上来,System V版本当中的信号量。信号量的本质相当于一个计数器,是描述保护临界资源中资源的数目。其目的是保护临界资源。但是System V版本的信号量是应用于进程间的。而POSIX信号量是可以用于线程间的。
#include
int sem_init(sem_t* sem, int pshared, unsigned int value);
//功能:初始化信号量
//参数:pshared:0表示线程间,非零表示进程间
//value:信号量初始值
int sem_destroy(sem_t* sem);
//功能:销毁信号量
int sem_wait(sem_t* sem);
//功能:等待信号量,相当于P操作
int sem_post(sem_t* sem);
//功能:发布信号量,相当于V操作
我们在上篇博客的生产者消费者模型中,其中生产者慢,消费者快,如果不加条件变量,那么消费者会长期占有互斥锁,从而导致死锁问题。而现在,如果消费者慢,生产者快,那么生产者就会生成大量的数据,导致消费者无法取出数据。从而造成不可估量的影响。倘若我们现在的环境是在一个环形队列当中。这个时候,如果生产者生产速度过快,则会导致消费者的取数据的速度跟不上生产速度,那么就会导致数据被覆盖。
#include
#include
#include
#include
#include "seqqueue.h"
pthread_mutex_t mutex;
void* consumers(void* arg)
{
SeqQueue* q = (SeqQueue*)arg;
char top = '\0';
while(1) {
sleep(1);
pthread_mutex_lock(&mutex);
int ret = SeqQueueGetFront(q, &top);
SeqQueuePop(q);
if(ret == -1) {
printf("can not get data\n");
pthread_mutex_unlock(&mutex);
} else {
printf("consumers get data %c\n",top);
pthread_mutex_unlock(&mutex);
}
}
}
void* producers(void* arg)
{
SeqQueue* q = (SeqQueue*)arg;
char i = 'a';
while(1) {
usleep(100000);
pthread_mutex_lock(&mutex);
SeqQueuePush(q, i);
printf("procuders set data %c\n",i);
pthread_mutex_unlock(&mutex);
i++;
}
}
int main()
{
pthread_t tid1, tid2;
pthread_mutex_init(&mutex, NULL);
SeqQueue q;
SeqQueueInit(&q);
pthread_create(&tid1, NULL, consumers, (void*)&q);
pthread_create(&tid2, NULL, producers, (void*)&q);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_mutex_destroy(&mutex);
return 0;
}
而这时候引入POSIX信号量就会避免这个问题,它能够让我们生产者与消费者同步起来。
#include
#include
#include
#include "seqqueue.h"
#include
pthread_mutex_t mutex;
sem_t con;
sem_t pro;
void* consumers(void* arg)
{
SeqQueue* q = (SeqQueue*)arg;
char top = '\0';
while(1) {
sleep(1);
sem_wait(&con);
pthread_mutex_lock(&mutex);
int ret = SeqQueueGetFront(q, &top);
SeqQueuePop(q);
if(ret == -1) {
printf("can not get data\n");
pthread_mutex_unlock(&mutex);
sem_post(&pro);
} else {
printf("consumers get data %c\n",top);
pthread_mutex_unlock(&mutex);
sem_post(&pro);
}
}
}
void* producers(void* arg)
{
SeqQueue* q = (SeqQueue*)arg;
char i = 'a';
while(1) {
usleep(100000);
sem_wait(&pro);
pthread_mutex_lock(&mutex);
SeqQueuePush(q, i);
printf("procuders set data %c\n",i);
pthread_mutex_unlock(&mutex);
sem_post(&con);
i++;
}
}
int main()
{
pthread_t tid1, tid2;
pthread_mutex_init(&mutex, NULL);
sem_init(&con, 0, 0);
sem_init(&pro, 0, 1);
SeqQueue q;
SeqQueueInit(&q);
pthread_create(&tid1, NULL, consumers, (void*)&q);
pthread_create(&tid2, NULL, producers, (void*)&q);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
pthread_mutex_destroy(&mutex);
sem_destroy(&con);
sem_destroy(&pro);
return 0;
}
现在我们发现,在添加了信号量以后,我们消费者与生产者尽管生产者速度很快,但是消费者仍然与生产者保持同步。
我们在编程的时候,某一些公共数据修改的机会很少。相比较修改,读的机会反倒很多。通常而言,读这个过程常常伴随着查找的操作,查找是一个耗时非常久的动作。如果给读的代码加锁,有可能就会出现死锁问题,由于读的时候太长了,导致无法写入。所以处理这种情况,我们就会加入一个新的方法,就是读写锁!
读写锁本质上是一种自旋锁。(所谓自旋锁,就是在等待的时候并不是阻塞等待,而是可以允许去做一些别的事情)
#include
int pthread_rwlock_init(pthread_rwlock_t* restrict relock, const pthread_rwlockattr_t* restrict attr);
//功能:初始化读写锁
int pthread_rwlock_destroy(pthread_rwlock_t* rwlock);
//功能:销毁读写锁
int pthread_rwlock_ldlock(pthread_rwlock_t* rwlock);//写加锁
int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock);//读加锁
int pthread_rwlock_unlock(pthread_rwlock_t* rwlock);//解锁
欢迎大家共同讨论,如有错误及时联系作者指出,并改正。谢谢大家!