`cond_broadcast` 是如何工作的?

Posted

技术标签:

【中文标题】`cond_broadcast` 是如何工作的?【英文标题】:How `cond_broadcast` works? 【发布时间】:2021-11-30 02:00:14 【问题描述】:

我在理解互斥锁时遇到了问题,例如让我们考虑一下我书中的以下代码:

int students[2] = 0;
cond_t conds[2];
mutex_t global;
void onArrival(int faculty) 
    mutex_lock(&global);
    int other = facolty ? 0:1;
    while(students[other]>0) cond_wait(&conds[facolty],&global);
    students[facolty]++;
    mutex_unlock(&global);

void onLeave(int faculty) 
    mutex_lock(&global);
    students[facolty]--;
    int other = facolty ? 0:1;
    cond_broadcast(&conds[other]);
    mutex_unlock(&global);

cond_broadcast 会唤醒所有等待的线程,但真正的问题是持续多长时间?我的意思是,执行第 16 行可能会唤醒 1000 个线程,但是当他们尝试捕获互斥锁时,它们会失败,因为第 17 行尚未执行......

那么在这种情况下会发生什么?

线程重新进入睡眠状态,在执行第 17 行时没有人会唤醒?

或者,他们会忙着等待直到互斥锁被释放?

【问题讨论】:

请不要使用代码图像。相反,将代码作为 text 粘贴到问题帖子中,并对其进行适当的格式化。这就是堆栈溢出的工作方式。另请参阅How to Ask。 @Tsyvarev Done,你现在能帮我吗? 这能回答你的问题吗? Which thread owns the associated mutex after pthread_cond_broadcast? cond_broadcast 将等待线程转换为状态,类似于mutex_lock 调用中的一个。也就是说,如果互斥锁被锁定,那么所有的等待者都保持在等待状态,但是那个时候他们将等待mutex,而不是condition 【参考方案1】:

cond_broadcast 将唤醒所有等待的线程,但真正的 问题是多久?我的意思是第 16 行可能会发生 已执行,唤醒了 1000 个线程

是的,从某种意义上说,它将他们从等待 CV 转变为争夺互斥锁。在他们成功获取互斥锁之前,他们实际上可能有资格也可能没有资格调度或接收任何 CPU 时间(感谢@DavidSchwartz 的澄清)。

但是当他们试图抓住 mutex 他们失败了,因为第 17 行还没有执行......

没有。线程不会无法获取互斥锁,至少不是因为这个原因。他们可能只需要等待才能获得它。

那么在这种情况下会发生什么?

线程重新进入睡眠状态,在执行第 17 行时没有人会唤醒?

或者,他们会忙着等待直到互斥锁被释放?

这取决于互斥锁的实现,但一般来说,线程会尝试获取互斥锁block,直到它们可以这样做。他们不会忙着等待。无论他们是显式获取互斥锁,还是在等待 CV 被唤醒后获取互斥锁,都是完全相同的。

请注意,任何阻塞尝试获取互斥锁的线程与等待来自 CV 的信号时的状态不同。他们正在争夺互斥锁,而之前没有,并且在继续之前他们不需要来自 CV 的另一个信号。

这是一个有点精明的问题,因为这里确实考虑了现实生活中的性能。使用允许向 CV 发送信号或广播而无需锁定相关互斥锁的 CV 实现,例如pthreads,在互斥锁解锁的情况下执行此操作会更有效,这样至少一个线程可以立即继续,而不是醒来,然后立即再次阻塞。但这是一个效率问题,而不是语义问题。

【讨论】:

谢谢你,@DavidSchwartz。我已将您的说明的一个版本纳入此答案。 你有一个明显的矛盾:“但一般来说,线程试图获取互斥块直到他们可以这样做。他们不会忙着等待。” 这有什么矛盾的,@algo?

以上是关于`cond_broadcast` 是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章

逗号运算符如何工作

oAuth 2 如何工作?

元组 (a,b)=(b,a) 中的成员交换如何在内部工作?

Haskell中()的默认定义如何工作?

工作表1里A关联着B,工作表2里B关联着C,工作表3里如何让C自动关联A。请大神级的人物给予解答,跪谢了!

春云侦探是如何工作的?