Linux:浅析线程(POSIX信号量与读写锁)

说到信号量,我们立即反应上来,System V版本当中的信号量。信号量的本质相当于一个计数器,是描述保护临界资源中资源的数目。其目的是保护临界资源。但是System V版本的信号量是应用于进程间的。而POSIX信号量是可以用于线程间的。

POSIX信号量函数

#include <semaphore.h>

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 <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#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 <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include "seqqueue.h"
#include <semaphore.h>

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 <pthread.h>

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);//解锁

欢迎大家共同讨论,如有错误及时联系作者指出,并改正。谢谢大家!

猜你喜欢

转载自blog.csdn.net/liuchenxia8/article/details/80016735