在一个条件变量需要的时候,是不是有足够的互斥?(When is a condition variab

2019-06-18 06:34发布

我敢肯定,互斥体是不够的条件变量的概念存在的理由; 但它打败我,我无法说服自己与一个具体的场景,当一个条件变量是必不可少的。

条件变量之间的差异,互斥和锁定问题的接受的答案说,一个条件变量是

具有“信令”机构锁定。 当线程需要等待资源可用时使用。 一个线程可以在CV“等待”,然后资源生产者可以“信号”的变量,在这种情况下,谁等待CV线程得到通知,并可以继续执行

当我感到困惑的是,一个线程可以在一个互斥等待太久,当它被暗示,被简单地意味着变量现在可用,为什么我需要一个条件变量?

PS:另外,一个互斥体还是需要例行守卫条件变量,当使我的视野更加歪朝没有看到条件变量的目的。

Answer 1:

即使你可以在你所描述的方式使用它们,互斥的设计并不适合用作通知/同步机制。 他们是为了提供对共享资源的互斥访问。 使用互斥信号的条件是尴尬的,我想会是这个样子(其中线程1被线程2信号):

线程1:

while(1) {
    lock(mutex); // Blocks waiting for notification from Thread2
    ... // do work after notification is received
    unlock(mutex); // Tells Thread2 we are done
}

线程2:

while(1) {
    ... // do the work that precedes notification
    unlock(mutex); // unblocks Thread1
    lock(mutex); // lock the mutex so Thread1 will block again
}

有几个问题是:

  1. 线程2不能继续“做之前通知工作”,直到线程1已与“通知后做事”的结束。 通过这种设计,线程2甚至没有必要的,那就是为何不动“先于工作”和“通知后做事”到同一个线程,因为只有一个可以在给定的时间运行!
  2. 如果线程2不能抢占线程1,线程1将立即当重复而(1)循环重新锁定互斥体和线程1会去这样做的“工作通知后,”即使没有通知。 这意味着你必须以某种方式保证线程1做之前线程2将锁定互斥。 你是怎样做的? 也许迫使日程事件在睡眠或其他一些特定于操作系统的方法,但即使这样,不能保证依赖于时间,您的操作系统,并调度算法的工作。

这两个问题是不小的,事实上,它们都是重大设计缺陷和潜在的错误。 这两个问题的起源是一个互斥被锁定,在同一个线程中打开了要求。 那么,你如何避免上述问题? 使用条件变量!

顺便说一句,如果您同步的需求是非常简单的,你可以使用一个普通的老式信号灯避免了条件变量的额外的复杂性。



Answer 2:

互斥是用于共享资源的独占访问,而条件变量是用于等待一个条件为真。 人们可能会认为他们可以实现条件变量不支持的内核。 常见的解决方法人们可以想出的是“标志+互斥体”是这样的:

lock(mutex)

while (!flag) {
    sleep(100);
}

unlock(mutex)

do_something_on_flag_set();

但它永远不会工作,因为你永远在等待过程中释放互斥锁,没有人能在一个线程安全的方式设置标志。 这就是为什么我们需要条件变量,当你在一个条件变量的等待,直到它的信号关联的互斥锁不是由你的线程持有。



Answer 3:

我在想这也和最重要的信息,这是我失踪到处是互斥可以自己(和变化)当时只有一个线程。 所以,如果你有一个生产者和更多的消费者,生产商将不得不等待互斥量来生产。 随着COND。 变量,他可以在任何时间产生。



Answer 4:

条件var和互斥对可以通过一个二进制信号量和互斥对被替换。 消费者线程的操作使用条件VAR +互斥时的顺序是:

  1. 锁定互斥

  2. 等待条件VAR

  3. 处理

  4. 解锁互斥

操作的生产者线程序列

  1. 锁定互斥

  2. 信号条件VAR

  3. 解锁互斥

使用SEMA +互斥对当相应的消费者线程序列是

  1. 等待的二进制SEMA

  2. 锁定互斥

  3. 检查预期的条件

  4. 如果条件为真,工艺。

  5. 解锁互斥

  6. 如果在步骤3中的条件检查是假的,回到第1步。

对于生产者线程的顺序是:

  1. 锁定互斥

  2. 发布二进制SEMA

  3. 解锁互斥

正如可以在步骤3中看到的无条件的处理的情况下使用条件VAR由条件处理,在步骤3和步骤4中使用二进制SEMA时更换。

其原因是,使用SEMA +互斥的时候,在一个争用条件,另一个消费者线程可以在步骤1和2与过程之间潜入/抵消的条件。 使用条件VAR时,这不会发生。 当使用条件VAR,病情guarantied步骤2之后是真实的。

二进制信号可与规则计数信号所取代。 这可能导致在步骤6到步骤1循环几次。



Answer 5:

您需要条件变量,将与互斥(各cond.var。属于一个互斥)转换成信号改变状态(条件)使用从一个线程到另一个。 这个想法是,一个线程可以等待,直到某些条件为真。 这样的条件是程序特定的(即“队列为空”,“矩阵为大”,“一些资源几乎耗尽”,“一些计算步骤已结束”等)。 互斥可能有几个相关的条件变量。 而你需要条件变量,因为这样的条件可能并不总是表现为简称为“一个互斥被锁定”(所以你需要播出条件,其他线程的变化)。

阅读一些很好的POSIX线程教程,比如本教程或这或那一个。 更妙的是,读好并行线程的书。 见这个问题 。

还可以阅读先进的Unix编程和高级Linux程序

PS并行和线程是困难的概念把握。 花时间阅读和实验,然后再次读取。



Answer 6:

我认为这是实现定义。
互斥锁足够与否取决于你是否认为互斥为临界区或更多的东西的机制。

正如上文http://en.cppreference.com/w/cpp/thread/mutex/unlock ,

互斥锁必须执行的当前线程被锁定,否则,行为是不确定的。

这意味着一个线程只能解锁C ++本身拥有的其锁定的互斥/。
但在其他编程语言,你也许可以在进程之间共享一个互斥体。

因此,区分这两个概念可能只是性能方面的考虑,一个复杂的所有权标识或进程间共享是不值得的简单应用。


例如,您可能会解决@ slowjelj的情况下,与另外互斥(这可能是一个不正确的修复):

线程1:

lock(mutex0);
while(1) {
    lock(mutex0); // Blocks waiting for notification from Thread2
    ... // do work after notification is received
    unlock(mutex1); // Tells Thread2 we are done
}

线程2:

while(1) {
    lock(mutex1); // lock the mutex so Thread1 will block again
    ... // do the work that precedes notification
    unlock(mutex0); // unblocks Thread1
}

但是,你的程序会抱怨说,已触发由编译器(如“无主互斥体的解锁”在Visual Studio 2015年)留下的断言。



文章来源: When is a condition variable needed, isn't a mutex enough?