Windows上的快速计数信号量
Fast counting semaphore on Windows?
首先,我知道它可以用互斥锁和条件变量来实现,但我想要最有效的实现。当没有争用时,我想要一个具有快速路径的信号量。在Linux上,这很容易使用futex;例如,下面是等待语句:
if (AtomicDecremenIfPositive(_counter) > 0) return; // Uncontended
AtomicAdd(&_waiters, 1);
do
{
if (syscall(SYS_futex, &_counter, FUTEX_WAIT_PRIVATE, 0, nullptr, nullptr, 0) == -1) // Sleep
{
AtomicAdd(&_waiters, -1);
throw std::runtime_error("Failed to wait for futex");
}
}
while (AtomicDecrementIfPositive(_counter) <= 0);
AtomicAdd(&_waiters, -1);
和职位:
AtomicAdd(&_counter, 1);
if (Load(_waiters) > 0 && syscall(SYS_futex, &_counter, FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0) == -1) throw std::runtime_error("Failed to wake futex"); // Wake one
起初我认为Windows只是使用NtWaitForKeyedEvent()。问题是它不是直接替换,因为它在进入内核之前不会自动检查_counter的值,因此可能会错过NtReleaseKeyedEvent()的唤醒。更糟糕的是,NtReleaseKeyedEvent()会阻塞。最好的解决方案是什么?
Windows自带CreateSemaphore信号量。除非您在使用常规方法时遇到某种记录的性能问题,否则您甚至不应该考虑脆弱的或特定于硬件的优化。
我认为这样的东西应该工作:
// bottom 16 bits: post count
// top 16 bits: wait count
struct Semaphore { unsigned val; }
wait(struct Semaphore *s)
{
retry:
do
old = s->val;
if old had posts (bottom 16 bits != 0)
new = old - 1
wait = false
else
new = old + 65536
wait = true
until successful CAS of &s->val from old to new
if wait == true
wait on keyed event
goto retry;
}
post(struct Semaphore *s)
{
do
old = s->val;
if old had waiters (top 16 bits != 0)
// perhaps new = old - 65536 and remove the "goto retry" above?
// not sure, but this is safer...
new = old - 65536 + 1
release = true
else
new = old + 1
release = false
until successful CAS of &s->val from old to new
if release == true
release keyed event
}
edit:这就是说,我不确定这会对你有多大帮助。您的线程池通常应该足够大,以便线程始终准备好处理您的请求。这意味着不仅要等待,而且帖子总是走慢路,到达内核。因此,计数信号量可能是您并不真正关心仅限用户空间的快速路径的一种原语。标准的Win32信号量应该足够好了。也就是说,我很高兴被证明是错的!
我赞成你的第一个想法,例如临界区和条件变量。临界区足够快,并且在休眠前使用联锁操作。或者,您可以尝试使用SRWLocks而不是临界区。条件变量(和SRWLocks)非常快——它们唯一的问题是XP上没有条件,但也许您不需要针对这个平台。
Qt有各种各样的东西,比如QMutex, QSemaphore,这些都是在精神上实现的,就像你在你的问题中提出的。
实际上,我建议用通常的操作系统提供的同步原语替换futex的东西;这应该无关紧要,因为这是一个缓慢的路径。
- 删除旧的信号量系统V
- 父进程和子进程之间的 POSIX 信号量
- 访问共享内存而不使用易失性、std::atomic、信号量、互斥锁和自旋锁
- 多线程.如果我使用信号量,我可以在开始时创建很多线程还是应该只有几个线程?
- C/C++ - 用于按顺序打印数字的 sem_t 类型的单个信号量
- 单车道桥 使用信号量进行同步
- 用于 64 位/32 位 IPC 的 POSIX 信号量的替代方案?
- 这个餐饮哲学家问题(dpp)的解决方案是如何工作的?互斥体和信号量
- 发布信号量返回错误 6(无效句柄)
- 在 C Linux 中使用三个线程使用信号量同步按顺序打印 3 4 5 50 次
- 在使用 pthread 和信号量实现生产者-消费者问题时需要帮助
- 如何让一个线程继续,而另一个线程正在等待C++中的信号量
- 实现信号量
- 计算信号量还是互斥体?
- POSIX 信号量在高争用/负载下不起作用
- C++:提升:托管共享内存是否需要信号量锁
- 如何使用Windows API直接将进程"assign"到信号量?
- Windows高速模式(C++):等待信号量(WaitForSingleObjectEx):失败,访问被拒绝[部分解决]
- Windows信号量和QSystemSemaphore
- Windows上的快速计数信号量