简而言之,手动重置事件是同步构造,其处于“信号”或“非信号”状态。在信号状态下,任何在事件上调用wait function的线程都不会阻塞,执行将继续不受影响。在非信号对象上调用等待函数的任何和所有线程都将阻塞,直到事件进入信号状态。
发信号和非信号状态之间的转换仅在显式调用SetEvent和ResetEvent等函数时才会发生。
我在Windows上构建了一个同步机制,它使用这些手动重置事件和它们的自动重置兄弟。使用信号量可以轻松复制自动复位机制,但我很难找到手动复位变体的等价物。
特别是,虽然具有“全部通知”功能的条件变量乍一看可能看起来相似,但是当您考虑到它需要关联的互斥锁这一事实时,它具有相当不同(可能是非功能性)的行为。首先,在线程可以在condvar上等待之前,它必须获取相关的互斥锁。除了获取和释放互斥锁的成本之外,这不必要地序列化所有即将等待的线程。在唤醒时,即使通知所有线程,一次只有一个线程实际上会获得互斥锁,从而产生额外的性能和并发性惩罚,因为在这种情况下互斥锁没有用处。
多CPU系统的发布情况特别差,因为所有服务员同时发布可以保证condvar和Windows事件之间的区别是可观察的 - 一个事件,在N个线程上将变为可运行的N CPU系统,并且可以并行运行,而使用condvar - 即使有一个避免雷鸣般的群体的实现 - 线程只能通过相关的互斥锁一次泄漏一个。
任何指向更好地模仿手动重置事件行为的构造的指针都将非常感激。我能找到的最接近的是一个屏障 - 这允许不同步的方法和多个线程的释放到屏障 - 但屏障“中断”基于等待线程计数而不是显式应用程序调用,这是我需要的。
答案 0 :(得分:3)
它仅限Linux(我只提及它,因为你有一个“linux”标签),但我认为你可以在futex
系统调用上构建类似的内容。有关血腥的详细信息,请参阅Ulrich Drepper's paper on Futexes。
非常粗略地说,我设想的是
void inline gate_wait(volatile int *gate)
{
if (*gate)
while (futex(gate, FUTEX_WAIT, 1, 0, 0, 0) == EINTR)
;
}
int inline gate_open(volatile int *gate)
{
*gate = 0;
return futex(gate, FUTEX_WAKE, INT_MAX, 0, 0, 0);
}
void inline gate_close(volatile int *gate)
{
*gate = 1;
}
如果你在futexes之上构建这个“gate”同步原语,那么可能值得将它贡献给Rusty Russell的用户空间futex库。
答案 1 :(得分:1)
如果所有设置/重置操作都在单线程中,您可以轻松使用管道: 管道为空/非空时,“事件”未发出信号/发出信号。 要阻止“事件”,请在管道的读取端使用select或poll。 设置“事件”将一个字节写入写端。 要重置“事件”,只需从读取端读取字节即可清空管道。
答案 2 :(得分:0)
读者作家会锁定工作吗?我怀疑它会,但你必须在你感兴趣的情况下检查它是否有效。
查看rwlock中的内容 http://www.opengroup.org/onlinepubs/000095399/basedefs/pthread.h.html
任意数量的读者只能使用一个编写器,因此要阻止其他线程,请执行写锁定。让他们继续,解锁。您必须查看哪种情况更有效:在已经存在至少一个读锁定或进行第一次读锁定时进行读锁定。我猜是前者,所以在让所有线程通过写解锁后,你可以进行读锁定。
如果你的线程在完成后没有读取 - 解锁,你将不得不做一些不同的事情,或者至少搞乱实现内部。如果累积了2 ^ 31个读锁,则不希望一次递减一个计数,直到可以进行写锁定。
哦,是的,caf的想法解决了这个问题:门是read_lock; read_unlock;
所以你不会建立读锁。
答案 3 :(得分:0)
我相信你要找的是你的pthreads库中的pthread_cond_ *函数。 Pthread Cond Functions
这些应该为您提供与Windows手动重置事件等效的内容。
答案 4 :(得分:0)
根据所需的事件功能,this article建议使用pthread条件或POSIX信号量。