【Linux】线程----POSIX信号量

POSIX信号量

    • 作用
    • 本质
        • 实现同步
        • 实现互斥
    • 信号量的应用
    • 信号量和条件变量的区别
    • 信号量和互斥锁的区别

作用

  • 实现进程、线程间的同步互斥

本质

  • 一个计数器 + pcb等待队列

实现同步

  1. 计数器对资源数量进行计数,当线程想要获取资源的时候,先访问信号量,判断是否能够获取(信号量通过自身的计数完成判断)
  2. 若计数<=0则直接阻塞线程,计数-1;
  3. 其它线程生产资源之后,计数+1,唤醒等待队列上的pcb

实现互斥

  • 保证计数器的数值不会大于1,就表示同一时间只有一个线程能够访问资源;

操作接口:

  1. 定义信号量
    sem_t sem;
  2. 初始化信号量
    int sem_init(sem_t *sem, int pshared, int value)
    pshared:信号量既可以用在进程间也可以用于线程间,pshared为0表示用于线程间,非0表示用于进程间
    value:信号量就是一个计数器,统计资源数量,value通过资源数量初始化计数器
  3. 在访问临界资源之前,先判断计数,是否能够访问资源
    若不能访问,则阻塞线程;
    若可以访问则调用int sem_wait(sem_t *sem) / int sem_trywait(sem_t *sem) / int sem_timedwait(sem_t *sem, struct timespec *ts)
  4. 访问临界资源之后/生产资源之后,唤醒一个等待的线程,并且计数器+1
    int sem_post(sem_t *sem);
  5. 不使用信号量记得释放资源
    int sem_destroy(sem_t *sem);

信号量的应用

通过信号量实现一个环形队列,最终实现一个生产者消费者模型

#include 
#include 
#include 
#include 
#include 

#define MAX_QUEUE 5
#define MAX_THREAD 5

class RingQueue{
     
  private:
    std::vector<int> _queue;
    int _capcity;
    int _step_read; // 当前读取位置下标
    int _step_write; // 当前写入位置下标

    sem_t _sem_lock; // 用于实现互斥的信号量
    sem_t _sem_data; // 对当前队列中的数据资源的数量进行计数;如果<=0表示没有资源,则会陷入等待
    sem_t _sem_space; // 对当前队列中空闲空间数量进行计数;如果<=0表示队列满了,则生产者陷入等待

  public:
    RingQueue(int max = MAX_QUEUE): _queue(max), _step_read(0), _step_write(0){
     
     sem_init(&_sem_lock, 0, 1); // 0表示用于线程间;初值为1
     sem_init(&_sem_data, 0, 0); // 0:没有数据
     sem_init(&_sem_space, 0, max); // max:全部都是空
    }
  ~RingQueue(){
     
    sem_destroy(&_sem_lock);
    sem_destroy(&_sem_data);
    sem_destroy(&_sem_space);
  }
  bool Push(int data){
     
    sem_wait(&_sem_space); // 统计空间节点数量,自动判断 是否有空闲空间,没有则阻塞
    sem_wait(&_sem_lock); // 若先加锁再判断,有可能会陷入休眠而没解锁

    _queue[_step_write] = data;
    _step_write = (_step_write + 1) % _capcity;

    sem_post(&_sem_lock);
    sem_post(&_sem_data); // 入队数据之后,数据资源的数量增加一个,并且唤醒消费者

    return true;
  }
  bool Pop(int *data){
     
    sem_wait(&_sem_data); // 消费者判断数据资源数量,若<=0则陷入等待,计数-1
    sem_wait(&_sem_lock);

    *data = _queue[_step_read];
    _step_read = (_step_read + 1) % _capcity;

    sem_post(&_sem_lock);
    sem_post(&_sem_space); // 空间资源多了一个,唤醒生产者

    return true;
  }
};

void *thr_consumer(void *arg){
     
  RingQueue *r = (RingQueue*)arg;
  while(1){
     
    int data;
    r->Pop(&data);
    printf("Consumer : %p data : %d\n", pthread_self(), data);
  }
}

void *thr_productor(void *arg){
     
  RingQueue *r = (RingQueue*)arg;
  int i = 0;
  while(1){
     
    r->Push(i);
    printf("Productor : %p data : %d\n", pthread_self(), i++);
  }
  return NULL;
}

int main()
{
     
  pthread_t ctid[MAX_THREAD], ptid[MAX_THREAD];
  int ret, i;
  RingQueue r;

  // 创建消费者线程
  for(i = 0; i < MAX_THREAD; i++){
     
    ret = pthread_create(&ctid[i], NULL, thr_consumer, (void*)&r);
    if(ret != 0){
     
      printf("thread creat error!\n");
      return -1;
    }
  }
  // 创建生产者线程
  for(i = 0; i < MAX_THREAD; i++){
     
    ret = pthread_create(&ptid[i], NULL, thr_productor, (void*)&r);
    if(ret != 0){
     
      printf("thread creat error!\n");
      return -1;
    }
  }
  // 等待所有线程退出
  for(i = 0; i < MAX_THREAD; i++){
     
    pthread_join(ctid[i], NULL);
    pthread_join(ptid[i], NULL);
    }
  return 0;
}

信号量和条件变量的区别

  1. 条件变量促使线程等待的条件判断需要用户自己完成,而信号量是通过自身计数进行判断的
  2. 条件变量需要搭配互斥锁一起使用,但信号量不需要

信号量和互斥锁的区别

  • 信号量是对资源进行计数的计数器,主要是用于实现同步的;而互斥锁只能实现互斥

你可能感兴趣的:(多线程,并发编程,操作系统,posix,c++)