是否保证pthread_cond_信号将唤醒正在等待的线程?

是否保证pthread_cond_信号将唤醒正在等待的线程?,c,pthreads,condition-variable,C,Pthreads,Condition Variable,这是一个一般性问题。例如,当前有两个子线程调用了pthread\u cond\u wait(&cond1,&mutex),它们都在等待。然后,父线程调用 pthread_cond_signal(&cond1); pthread_cond_signal(&cond1); 接下来,我的问题是,是否保证两个等待的线程都会被唤醒?(假设被唤醒的第一个线程稍后在执行的某个阶段释放互斥,以便第二个线程可以获取互斥) 我问这个问题的原因是,对于Unix系统级信号,信号(比如说SIGCHLD)

这是一个一般性问题。例如,当前有两个子线程调用了
pthread\u cond\u wait(&cond1,&mutex)
,它们都在等待。然后,父线程调用

pthread_cond_signal(&cond1);
pthread_cond_signal(&cond1);
接下来,我的问题是,是否保证两个等待的线程都会被唤醒?(假设被唤醒的第一个线程稍后在执行的某个阶段释放互斥,以便第二个线程可以获取互斥)


我问这个问题的原因是,对于Unix系统级信号,信号(比如说
SIGCHLD
)没有排队,因此如果连续发送,相同类型的多个信号可能会丢失。因此,我想知道
pthread\u cond\u signal
是否以不同的方式实现,以便在调度程序让父线程连续发出两次信号时不会丢失它们?

快速回答:

pthread\u cond\u signal()
将唤醒至少一个在条件变量上被阻止的线程,但不能保证唤醒的线程超过此数目(参考,请使用
pthread\u cond\u broadcast()
唤醒所有被阻止的线程)

发件人:

pthread_cond_signal()调用至少取消阻止一个线程 在指定的条件变量cond(如果有)上被阻止的 线程在cond上被阻塞

pthread_cond_broadcast()调用当前取消阻止所有线程 在指定的条件变量cond上被阻止

较长的答案:

因此,根据规范,我假设解除阻塞是同步发生的,也就是说,通过第一次调用
pthread\u cond\u signal()
解除阻塞的线程将被第二次调用
pthread\u cond\u signal()
解除阻塞,从而唤醒另一个线程

但是,我不知道您的特定pthread实现是否是这样(而且glibc网站目前非常危险,因此无法访问代码查看)

可能尚未实现,但它在规范答案中:

但是应该注意的是,该规范最近稍微修改了一下,
pthread\u cond\u signal()
pthread\u cond\u broadcast()
如何确定在给定条件变量上实际阻塞了哪些线程,但我认为并非所有的实现都能跟上

可以找到关于该主题的长期讨论,新规范为:

pthread_cond_broadcast()和pthread_cond_signal()函数 应自动确定堵塞的螺纹(如有) 在指定的条件变量cond上。这个决心 应在试验期间的未指定时间发生 pthread_cond_broadcast()或pthread_cond_signal()调用。 pthread_cond_broadcast()函数随后将解除对所有 这些线。pthread_cond_signal()函数应在 这些线程中至少有一个

因此,结论是: 如果不是规范的专家解释者,我会说新的文本支持这种同步发生的假设——这样两个连续调用
pthread\u cond\u signal()
并有两个阻塞线程可用,就会唤醒两个线程


不过,我不是100%确定,所以如果有人能详细说明,请尽管这样做。

我知道这是一个古老的线程(没有双关语),但典型的实现是这样工作的:

一个条件变量将包含一个当前处于休眠状态的线程队列,等待它收到信号

锁将有一个线程队列,这些线程已进入睡眠状态,因为它们试图获取它,但被另一个线程持有

cond_wait将正在运行的线程添加到条件变量的队列中,释放锁,并将其自身置于睡眠状态

cond_信号只是将一个休眠线程从条件变量的队列移动到锁的队列

当运行线程释放锁时,将从锁的队列中移除一个休眠线程,锁的所有权转移到该休眠线程,并唤醒该休眠线程


不要问我为什么规范会说cond_信号可能会唤醒多个线程…

查看
pthread_cond_信号()
实现,有一条注释简要解释了代码的作用:

加载服务员序列号,它表示我们对任何服务员的相对顺序。放松的MO足以满足这一要求,因为:

  • 我们可以在约束发生之前选择外部允许的任何位置。特别是,如果在我们之前发生另一个
    \u pthread\u cond\u wait
    呼叫,该服务员必须有资格被我们叫醒。唯一的方法是在获取与
    condvar
    相关联的互斥量的同时发送信号,并确保信号的关键部分发生在等待之后。因此,互斥锁确保我们看到服务员的
    \uuuwseq
    增加
  • 一旦我们选择了一个位置,我们就不需要通过设置之前发生的事件向程序传达这一点:首先,任何唤醒都可能是虚假的唤醒,因此程序不得将唤醒解释为服务员在特定信号之前发生的指示;其次,程序无法检测服务员是否尚未被叫醒(即,它无法区分未被叫醒的服务员和已被叫醒但尚未恢复执行的服务员),因此无法尝试推断信号发生在特定服务员之前
  • 如前所述,在
    pthread\u cond\u wait()
    实现中有关于该算法的更多信息:

    这个
    condvar
    实现保证了对信号和广播的所有调用,以及对
    pthread_mutex_lock(mutex);
    ...
    pthread_cond_signal(cond1);    // no mutex reference, these calls could happen
    pthread_cond_signal(cond1);    // while the mutex is not locked...
    pthread_cond_unlock(mutex);
    
    pthread_mutex_lock(mutex);
    ...
    pthread_cond_wait(cond1, mutex);
    ...
    pthread_mutex_unlock(mutex);
    
    #define __PTHREAD_COND_MAX_GROUP_SIZE ((unsigned) 1 << 29)