zoukankan      html  css  js  c++  java
  • 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. 最好使用其他方式替代信号量,比如条件变量;
  • 相关阅读:
    无重复字符的最长子串
    最长公共前缀
    项目开发的 工程化
    包管理 import debug 模块管理 module
    Third Party Browser Drivers NOT DEVELOPED by seleniumhq
    任何不看源码的代码引入都是存在定时爆炸的可能
    博客数计数
    lineage 世系 血缘 容错机制 DAG
    查源码分析 游标 写 需要 cursors 一切不看源码的代码引入都是定时炸弹的启动
    8核 16g 及时释放内存空间
  • 原文地址:https://www.cnblogs.com/wanpengcoder/p/11767242.html
Copyright © 2011-2022 走看看