• 操作系统:进程通信信号量和互斥量


    参考:

    https://blog.csdn.net/benjamin721/article/details/50703692(进程/线程通信:信号量和互斥量总结)

    https://blog.csdn.net/qq_46275568/article/details/120630019(信号量及其应用)

    https://www.jianshu.com/p/6674400a98b2(信号量与PV操作)

    信号量的引入

    由于对互斥量的操作在cpu执行时不是原子性的,如果多个进程/线程同时访问互斥量,被cpu分解为一个一个指令时有重叠,就会出问题。由此引入信号量!

    信号量:

    信号量s是非负整数值的全局变量。只能有两种特殊的操作,这两种操作称为P和V,(再加上一个信号量的初始化)

    P(s):如果 s 是非零的,那么 P 将 s 减 1,并且立即返回。(如果 s 为零,那么就挂起这个线程,直到 s 变为非零)。
    
    V(s): V 操作将 s 加 1。(如果有任何线程阻塞在 Р 操作等待s变成非零,那么 V 操作会重启这些线程中的一个)。
     

    用PV操作解决并发互斥问题

    用PV操作,解决进程间互斥问题有以下4点:
    1.划定临界区
    2.对多个进程的临界区,设定一个信号量mutex。mutex这个信号量,初值为1。
    简单说明一下mutex,mutex是常用于解决互斥问题的时候所定义的一个信号量变量。是互斥(mutual exclusion)的缩写。现在已经形成一种约定俗成的互斥的命名。
    3.在临界区前实施P(mutex)
    4.在临界区之后实施V(mutex)

    信号量与互斥锁的区别:

    1. 互斥锁用于线程的互斥,信号量用于线程的同步

    互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。

    同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源

    2. 互斥锁量值只能为0/1,信号量值可以为非负整数。

    3. 互斥量的加锁和解锁必须由同一线程分别对应使用,信号量可以由一个线程释放,另一个线程得到。

    二值信号量也可以理解为互斥锁:show me the code

    #include<stdio.h>
    #include<pthread.h>
    #include<stdlib.h>
    #include<semaphore.h>
    
    // global shared varible
    volatile int cnt = 0; // counter
    
    sem_t mutex;
    
    void P(sem_t* s) {sem_wait(s);}
    void V(sem_t* s) {sem_post(s);}
    
    // thread routine
    void *thread(void* arg)
    {
      int i, niters = *((int*)arg);
      
      for(int i = 0; i < niters; ++i)
      {
        P(&mutex);
        cnt++;
        V(&mutex);
      }
      return NULL;
    }
    
    int main(int argc, char* argv[])
    {
      int niters;
      pthread_t tid1, tid2;  
      sem_init(&mutex, 0, 1); // mutex = 1,初始化为1,并且先P后V,那么mutex只可能市0或1,即为二值信号量
    
      if(argc != 2)
      {
        printf("usage: %s <niters>\n", argv[0]);
        exit(0);
      }
    
      niters = atoi(argv[1]);
      
      // create thread and wait for them to finish
      pthread_create(&tid1, NULL, thread, &niters);
      pthread_create(&tid2, NULL, thread, &niters);
      pthread_join(tid1, NULL);
      pthread_join(tid2, NULL);
      
      printf("cnt = %d\n", cnt);
      return 0;
    }

    生产者消费者模型(有界缓冲区问题)

    #include<stdio.h>
    #include<stdlib.h>
    #include<pthread.h>
    #include<unistd.h>
    #include<semaphore.h>
    #include<time.h>
    
    typedef struct
    {
      int *buf;  
      int n;
      int front; // 指向第一个元素前面的位置
      int rear;  // 指向最后一个元素
      sem_t mutex;
      sem_t slots;
      sem_t items;
    }sbuf_t;
    
    // PV操作
    void P(sem_t *s){sem_wait(s);}
    void V(sem_t *s){sem_post(s);}
    
    // 创建一个大小为 n 个槽,空的缓冲区
    void sbuf_init(sbuf_t *sp, int n)
    {
      sp->buf = (int*)calloc(n, sizeof(int));
      sp->n = n;
      sp->front = sp->rear = 0;
      sem_init(&sp->mutex, 0, 1);
      sem_init(&sp->slots, 0, n);
      sem_init(&sp->items, 0, 0);
    }
    
    // 释放缓冲区
    void sbuf_deinit(sbuf_t *sp)
    {
      free(sp->buf);
    }
    
    // 向缓冲区中加入 item 
    void sbuf_insert(sbuf_t *sp, int item)
    {
      P(&sp->slots);
      P(&sp->mutex);
      sp->buf[++sp->rear % sp->n] = item;
      V(&sp->mutex);
      V(&sp->items);
    }
    
    // 从缓冲区中取出 item 
    int sbuf_remove(sbuf_t *sp)
    {
      int ret;
      P(&sp->items);
      P(&sp->mutex);
      ret = sp->buf[(++sp->front) % sp->n];
      V(&sp->mutex);
      V(&sp->slots);
    
      return ret;
    }
    
    void *producer1(void* arg)
    {
      while(1)
      {
        usleep(300); 
        int num = rand() % 10000;
        sbuf_insert((sbuf_t*)arg, num);
        printf("producer1 生产:%d\n", num);
      }
      return NULL;
    }
    
    void *consumer1(void* arg)
    {
      while(1)
      {
        sleep(1);
        printf("consumer1 消费 %d\n", sbuf_remove((sbuf_t*)arg));
      }
    
      return NULL;
    }
    
    void *consumer2(void* arg)
    {
      while(1)
      {
        sleep(2);
        printf("consumer2 消费 %d\n", sbuf_remove((sbuf_t*)arg));
      }
    
      return NULL;
    }
    
    int main()
    { 
      pthread_t tid1, tid2, tid3;
      sbuf_t sbuf;
      
      srand(time(NULL));
      sbuf_init(&sbuf, 5);
    
      pthread_create(&tid1, NULL, producer1, &sbuf);
      pthread_create(&tid2, NULL, consumer1, &sbuf);
      pthread_create(&tid3, NULL, consumer2, &sbuf);
      pthread_join(tid1, NULL);
      pthread_join(tid2, NULL);
      pthread_join(tid3, NULL);
      
      sbuf_deinit(&sbuf);
    
      return 0;
    }
  • 相关阅读:
    Linux直接在通过终端打开图片文件
    【暑假】[实用数据结构]UVa11995 I Can Guess the Data Structure!
    【暑假】[实用数据结构]动态范围查询问题
    【暑假】[实用数据结构]范围最小值问题(RMQ)
    【暑假】[实用数据结构]动态连续和查询问题
    【暑假】[基本数据结构]基本的数据结构知识点总结梳理
    【暑假】[基本数据结构]根据in_order与post_order构树
    【暑假】[基本数据结构]根据BFS与DFS确定树
    【暑假】[网络流]网络流知识总结
    [HDOJ2546] 饭卡 (01背包)
  • 原文地址:https://www.cnblogs.com/tkzc2013/p/15871440.html
Copyright © 2020-2023  润新知