假设在信令线程修改影响谓词真值的状态并调用pthread_cond_signal而不包含与条件变量关联的互斥的情况下使用条件变量?这种类型的用法是否确实是总是受可能漏掉信号的竞争条件的影响?
在我看来,似乎总有一场明显的竞赛:
pthread_cond_signal,它什么也不做,因为还没有服务生。pthread_cond_wait,不知道谓词现在为真,并无限期地等待。但是,这种竞争条件是否总是存在于这样的情况下:(A)在调用pthread_cond_signal时保持互斥,而不是在更改状态时保持互斥,或者(B)使互斥在改变状态时保持,而不是在调用pthread_cond_signal时保持。
我的观点是想知道上述非最佳实践惯例是否有任何有效的用途,即一个正确的条件变量实现是否需要考虑这些避免种族条件本身的用法,或者它是否可以忽略它们,因为它们本身就已经很有主导权。
发布于 2011-09-24 00:22:50
状态必须在互斥体中被修改,如果没有其他原因,那就是可能出现虚假的唤醒,这会导致读者在写状态的过程中阅读状态。
您可以在状态更改后随时调用pthread_cond_signal。它不一定要在互斥体里面。POSIX保证至少有一名侍者会醒来检查新的州。更重要的是:
pthread_cond_signal并不能保证读取器首先获得互斥锁。在读者有机会检查新的状态之前,另一个作者可能会进来。条件变量不能保证读取器立即跟随作者(毕竟,如果没有读取器怎么办?)编辑: @DietrichEpp在评论中提出了一个很好的观点。作者必须改变状态,使读者永远无法访问不一致的状态。它可以通过获取条件变量中使用的互斥(如我前面所指出的),或者通过确保所有状态变化都是原子性的。
发布于 2012-03-29 03:39:05
答案是,有一个种族,要消除这个种族,你必须这样做:
/* atomic op outside of mutex, and then: */
pthread_mutex_lock(&m);
pthread_mutex_unlock(&m);
pthread_cond_signal(&c);数据的保护并不重要,因为在调用pthread_cond_signal时并不持有互斥对象。
看,通过锁定和解锁互斥锁,您已经创建了一个屏障。在信号者拥有互斥的短暂时刻,有一个确定的事实:没有其他线程有互斥。这意味着没有其他线程正在执行任何关键区域。
这意味着所有线程要么将获得互斥锁来发现您发布的更改,要么它们已经找到该更改并与其一起跑掉(释放互斥对象),或者没有找到它们正在寻找的并且已经原子地放弃互斥锁进入休眠状态(并且保证会很好地等待此条件)。
没有互斥锁/解锁,就没有同步。信号有时会触发,因为没有看到更改的原子值的线程正在转换到它们的原子休眠以等待它。
所以,从线程的角度来看,这就是互斥体所做的事情,线程就是这样做的。您可以从其他地方获得访问的原子性,但不能获得同步。
我以前也执行过这个逻辑。这种情况发生在Linux内核中(使用我自己的互斥变量和条件变量)。
在我的情况下,信号者不可能为共享数据上的原子操作保留互斥体。为什么?因为信号在用户空间中执行操作,在内核和用户之间共享的缓冲区中,然后(在某些情况下)对内核进行系统调用以唤醒线程。用户空间只是对缓冲区做了一些修改,然后如果满足某些条件,它将执行一个ioctl。
因此,在ioctl调用中,我执行了互斥锁/解锁操作,然后按下条件变量。这确保线程不会错过与用户空间发布的最新修改相关的唤醒。
一开始我有条件变量信号,但是它看上去不像互斥体的参与,所以我对这种情况进行了一些思考,意识到互斥必须被锁定和解锁才能符合同步仪式,从而消除丢失的唤醒。
https://stackoverflow.com/questions/7536057
复制相似问题