• sem_init重复调用引发sem_wait线程无法被唤醒


    问题

    一段老代码,两个线程,一个线程调用sem_wait等待信号量,另外一个线程在某失败分支会调用sem_init清信号量,结果导致sem_wait线程无法被唤醒;

    分析

    Linux manpage

    从描述中可见,初始化一个已经被初始化的信号量会导致未定义行为;

     1 NAME
     2        sem_init - initialize an unnamed semaphore
     3 
     4 SYNOPSIS
     5        #include <semaphore.h>
     6 
     7        int sem_init(sem_t *sem, int pshared, unsigned int value);
     8 
     9        Link with -lrt or -pthread.
    10 
    11 DESCRIPTION
    12 ...
    13 
    14        Initializing a semaphore that has already been initialized results in undefined behavior.

    glibc源码

    到底会发生什么未定义行为,我们直接看源码吧;

    首先,对比结构体,旧结构体只有value成员,新结构体中增加了private和nwaiters成员;nwaiters成员会在调用sem_wait时候增加;

    然后,对比sem_post唤醒函数;可见,新唤醒函数会在唤醒操作执行之前对nwaiters进行判断,只有当nwaiters>0时,才进行唤醒;

    而旧的唤醒操作,则没有类似判断;

    现在,我们清楚了,老代码用的老版本的glibc,内部没有等待判断,一直没有出问题,而使用新版本的glibc之后,加入了判断,就有问题了;

    结论

    1. sem_init是用来在初始化的时候调用初始化信号量的,并不是用来将信号量清零的;
    2. 重复调用sem_init的行为可能导致已经处于sem_wait的线程无法被唤醒;
    3. 旧版本的glibc机制比较弱,所以老代码一直运行很好;但是新glibc作了检查,所以会出问题;
    4. 按照目前代码看,如果单个线程自己在调用了sem_wait之后再调用sem_init时没什么影响的;但是不保证以后的glibc会再做什么修改造成影响;
    5. 除了初始化阶段,其他流程中不要使用sem_init;
    6. 最好使用其他方式替代信号量,比如条件变量;
  • 相关阅读:
    函数三
    函数二
    函数一
    python数据类型和编码补充
    Python基础数据类型考试题
    VS2017设置C++标准
    std::weak_ptr
    Visual Assist 配色
    JMeter
    fcgi返回状态码
  • 原文地址:https://www.cnblogs.com/wanpengcoder/p/11767242.html
Copyright © 2020-2023  润新知