zoukankan      html  css  js  c++  java
  • Linux 线程】线程同步《四》

    1、信号量

    (1)概念

    信号量和互斥锁(mutex)的区别:互斥锁只允许一个线程进入临界区,而信号量允许多个线程同时进入临界区

    不多做解释,要使用信号量同步,需要包含头文件semaphore.h。

    主要用到的函数:

    • int sem_init(sem_t *sem, int pshared, unsigned int value);其中sem是要初始化的信号量,pshared表示此信号量是在进程间共享还是线程间共享,value是信号量的初始值。
    • int sem_destroy(sem_t *sem);其中sem是要销毁的信号量。只有用sem_init初始化的信号量才能用sem_destroy销毁。
    • int sem_wait(sem_t *sem);等待信号量,如果信号量的值大于0,将信号量的值减1,立即返回。如果信号量的值为0,则线程阻塞。相当于P操作。成功返回0,失败返回-1。
    • int sem_post(sem_t *sem); 释放信号量,让信号量的值加1。相当于V操作。

    (2)举例

    《举例1》

     1 /*************************************************************************
     2     > File Name: semTest1.c
     3     > Summary:  信号量实现生产者&消费者模型    
     4     > Author: xuelisheng 
     5     > Created Time: 2018年12月18日 
     6  ************************************************************************/
     7 
     8 #include <stdio.h>
     9 #include <unistd.h>
    10 #include <pthread.h>
    11 #include <stdio.h>
    12 #include <semaphore.h>
    13 
    14 #define NUM 5
    15 
    16 int queue[NUM];                                // 全局数组实现环形队列
    17 sem_t blank_number, product_number;            // 定义2个信号量:空格子信号量   产品信号量
    18 
    19 void *producer(void *arg)
    20 {
    21     int i = 0;
    22     while(1)
    23     {
    24         // int sem_post(sem_t *sem); 释放信号量,让信号量的值加1。相当于V操作。
    25         sem_wait(&blank_number);                // 生产者将空格字数 -- ,为0则阻塞等待
    26         queue[i] = rand() % 1000 + 1;           // 生产一个产品
    27         printf("-----producer-----%d          i = %d
    ",queue[i], i);
    28         sem_post(&product_number);              // 将产品数 ++   唤醒
    29 
    30         i = (i+1) % NUM;                        // 借助下标实现环形队列
    31         sleep(rand() % 1);                
    32     }
    33 }
    34 
    35  void *consumer(void *arg)
    36  {
    37      int i = 0;
    38      while(1)
    39      {
    40          // int sem_wait(sem_t *sem);等待信号量,如果信号量的值大于0,将信号量的值减1,立即返回。如果信号量的值为0,则线程阻塞。相当于P操作。成功返回0,失败返回-1。
    41          sem_wait(&product_number);            // 消费者将产品信号量数 --,为0则阻塞等待
    42          printf("-----consumer-----%d         i = %d
    ", queue[i], i);
    43          queue[i] = 0;                        // 消费一个产品(填充0表示)
    44          sem_post(&blank_number);             // 消费掉之后,将格子数 ++
    45 
    46          i= (i+1) % NUM;
    47          sleep(rand() % 3);
    48      }
    49  }
    50 
    51  int main()
    52  {
    53      pthread_t pid, cid;
    54 
    55      // int sem_init(sem_t *sem, int pshared, unsigned int value);,其中sem是要初始化的信号量,pshared表示此信号量是在进程间共享还是线程间共享(0表示线程),value是信号量的初始值。
    56      sem_init(&blank_number, 0, NUM);
    57      sem_init(&product_number, 0, 0);
    58 
    59      pthread_create(&pid, NULL, producer, NULL);
    60      pthread_create(&cid, NULL, consumer, NULL);
    61 
    62      pthread_join(pid, NULL);
    63      pthread_join(cid, NULL);
    64 
    65      // int sem_destroy(sem_t *sem); 其中sem是要销毁的信号量。只有用sem_init初始化的信号量才能用sem_destroy销毁。
    66      sem_destroy(&blank_number);
    67      sem_destroy(&product_number);
    68 
    69      return 0;
    70  }

    运行结果:

    -----producer-----384          i = 0
    -----consumer-----384         i = 0
    -----producer-----778          i = 1
    -----producer-----336          i = 2
    -----producer-----493          i = 3
    -----producer-----422          i = 4
    -----producer-----28            i = 0
    -----consumer-----778         i = 1
    -----producer-----764          i = 1
    -----consumer-----336         i = 2
    -----producer-----427          i = 2
    -----consumer-----493         i = 3
    -----producer-----212          i = 3
    -----consumer-----422         i = 4
    -----producer-----430          i = 4
    -----consumer-----28         i = 0
    -----producer-----863          i = 0

    《举例2》

     1 /*************************************************************************
     2     > File Name: semTest2.c
     3     > Summary:  基于信号量的多线程同步,操作系统原理中的P,V操作
     4     > Author: xuelisheng 
     5     > Created Time: 2018年12月18日
     6  ************************************************************************/
     7 #include <pthread.h>
     8 #include <semaphore.h>
     9 #include <unistd.h>
    10 #include <stdio.h>
    11 #include <stdlib.h>
    12 
    13 
    14 /* @Scene: 某行业营业厅同时只能服务两个顾客。
    15  * 有多个顾客到来,每个顾客如果发现服务窗口已满,就等待,
    16  * 如果有可用的服务窗口,就接受服务。 */
    17 
    18 /* 将信号量定义为全局变量,方便多个线程共享 */
    19 sem_t sem;
    20 
    21 /* 每个线程要运行的例程 */
    22 void * get_service(void *thread_id)
    23 {
    24     /* 注意:立即保存thread_id的值,因为thread_id是对主线程中循环变量i的引用,它可能马上被修改 */
    25     int customer_id = *((int *)thread_id);
    26 
    27     if(sem_wait(&sem) == 0)                         // 对共享资源的控制(每次只能2个线程对共享资源进行访问)
    28     {
    29         usleep(100);                /* service time: 100ms */
    30         printf("customer %d receive service ...
    ", customer_id);
    31         sem_post(&sem);
    32     }
    33 }
    34 
    35 #define CUSTOMER_NUM 10
    36 
    37 int main(int argc, char *argv[])
    38 {
    39     /* 初始化信号量,初始值为2,表示有两个顾客可以同时接收服务 */
    40     /* @prototype: int sem_init(sem_t *sem, int pshared, unsigned int value); */
    41     /* pshared: if pshared == 0, the semaphore is shared among threads of a process
    42      * otherwise the semaphore is shared between processes.   */
    43     sem_init(&sem, 0, 2);
    44 
    45     /* 为每个顾客定义一个线程id, pthread_t 其实是unsigned long int */
    46     // 定义10个线程
    47     pthread_t customers[CUSTOMER_NUM];
    48 
    49     int i, ret;
    50     /* 为每个顾客生成一个线程 */
    51     for(i = 0; i < CUSTOMER_NUM; i++)
    52     {
    53         int customer_id = i;
    54         ret = pthread_create(&customers[i], NULL, get_service, &customer_id);
    55         if(ret != 0)
    56         {
    57             perror("pthread_create");
    58             exit(1);
    59         }
    60         else 
    61         {
    62             printf("Customer %d arrived.
    ", i);
    63         }
    64         usleep(10);
    65     }
    66 
    67     /* 等待所有顾客的线程结束 */
    68     /* 注意:这地方不能再用i做循环变量,因为可能线程中正在访问i的值 */
    69     int j;
    70     for(j = 0; j < CUSTOMER_NUM; j++)
    71      {
    72         pthread_join(customers[j], NULL);
    73     }
    74 
    75     /* Only a  semaphore that  has been initialized  by sem_init(3)
    76      * should be destroyed using sem_destroy().*/
    77     sem_destroy(&sem);
    78     return 0;
    79 }

    运行结果:(结果不唯一)

    Customer 0 arrived.
    Customer 1 arrived.
    customer 0 receive service ...
    Customer 2 arrived.
    customer 1 receive service ...
    Customer 3 arrived.
    customer 2 receive service ...
    Customer 4 arrived.
    customer 3 receive service ...
    Customer 5 arrived.
    customer 4 receive service ...
    Customer 6 arrived.
    customer 5 receive service ...
    Customer 7 arrived.
    customer 6 receive service ...
    Customer 8 arrived.
    customer 7 receive service ...
    Customer 9 arrived.
    customer 8 receive service ...
    customer 9 receive service ...

    参考:https://www.cnblogs.com/jiqingwu/p/linux_semaphore_example.html

  • 相关阅读:
    vue学习6
    vue学习5
    vue学习3
    vue学习2
    vue学习1
    idea快速查找和替换快捷键
    mysql三元表达式
    1 Java Lambda表达式forEach无法跳出循环的解决思路
    6 Mybatis Plus and 和 or,分页Page使用
    4 Mybatis Plus使用redis作为二级缓存
  • 原文地址:https://www.cnblogs.com/xuelisheng/p/10144411.html
Copyright © 2011-2022 走看看