Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/128.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ Windows上的快速计数信号量?_C++_Multithreading_Winapi_Synchronization_Semaphore - Fatal编程技术网

C++ Windows上的快速计数信号量?

C++ Windows上的快速计数信号量?,c++,multithreading,winapi,synchronization,semaphore,C++,Multithreading,Winapi,Synchronization,Semaphore,首先,我知道它可以通过互斥和条件变量实现,但我希望实现的效率尽可能高。 我想要一个没有争用时具有快速路径的信号灯。在Linux上,使用futex很容易做到这一点;例如,这里有一个等待: if (AtomicDecremenIfPositive(_counter) > 0) return; // Uncontended AtomicAdd(&_waiters, 1); do { if (syscall(SYS_futex, &_counter, FUTEX_WAIT_

首先,我知道它可以通过互斥和条件变量实现,但我希望实现的效率尽可能高。 我想要一个没有争用时具有快速路径的信号灯。在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);
起初,我认为Windows只需使用NtWaitForKeyedEvent()。问题是它不是直接替换,因为它在进入内核之前不会自动检查_计数器的值,因此可能会错过NtReleaseKeyedEvent()的唤醒。更糟糕的是,NtReleaseKeyedEvent()会阻塞。
最好的解决方案是什么?

Windows有带的本机信号量。除非你有某种文档化的性能问题,否则就不应该考虑脆弱或硬件特定的优化。

Windows有本地信号量。除非你有某种文档化的性能问题,否则你不应该考虑脆弱或硬件特定的优化。

QT有QQueX、QSimPalm之类的各种各样的东西,它们都是像你在你的问题中所呈现的那样在精神上实现的。


实际上,我建议用操作系统提供的同步原语取代futex;这应该没什么大不了的,因为这是一条缓慢的道路。

Qt有各种各样的东西,比如QMutex、QSemaphore,它们在精神上实现了,就像你在问题中提出的那样


实际上,我建议用操作系统提供的同步原语取代futex;这并不重要,因为这是一条缓慢的道路。

我赞成你的第一个想法,例如临界截面和条件变量。临界段足够快,在进入睡眠状态之前,它确实使用联锁操作。或者,您可以使用SRWLock而不是critical section进行实验。条件变量(和SRWLock)非常快-它们唯一的问题是XP上没有条件,但您可能不需要针对这个平台。

我赞成您的第一个想法,例如临界截面和条件变量。临界段足够快,在进入睡眠状态之前,它确实使用联锁操作。或者,您可以使用SRWLock而不是critical section进行实验。条件变量(和SRWLock)非常快-它们唯一的问题是XP上没有条件,但可能您不需要针对这个平台。

我认为类似的方法应该可以工作:

// 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
}

编辑:也就是说,我不确定这会对你有多大帮助。您的线程池通常应该足够大,以便线程始终准备好处理您的请求。这意味着不仅等待,而且POST将始终采用缓慢的路径并进入内核。因此,计数信号量可能是一个您并不真正关心仅限于用户空间的快速路径的原语。库存Win32信号量应该足够好了。也就是说,我很高兴被证明是错的

我认为这样做应该行得通:

// 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
}

编辑:也就是说,我不确定这会对你有多大帮助。您的线程池通常应该足够大,以便线程始终准备好处理您的请求。这意味着不仅等待,而且POST将始终采用缓慢的路径并进入内核。因此,计数信号量可能是一个您并不真正关心仅限于用户空间的快速路径的原语。库存Win32信号量应该足够好了。也就是说,我很高兴被证明是错的

信号量限制对共享资源的并发访问次数。互斥锁序列化访问,因此并发用户必须等待。你想要哪个?信号灯。互斥锁应该由锁定它们的线程解锁。我需要线程等待其他人发布的信号量。。第一个想法:如果信号量计数由整数表示,则信号量上的原子减量(结果为负数)表示调用方需要等待。sema上导致零或负结果的原子增量表示存在需要释放的等待线程。在某些情况下,这会与保护线程等待的事件列表的“超级CS”一起使用更快的“无内核”路径生成更好的信号量吗?哦。。您是使用信号量与驱动程序通信,即受限系统调用,还是仅用于用户空间中的线程间通信?是的,使用信号量post块有点奇怪。但考虑到键控事件的使用方式,只有当服务员正好位于其快速路径(CAS)和对NtWaitForKeyedEvent的调用之间时才会发生这种情况,NtWaitForKeyedEvent应该是一个只有几个时钟周期的窗口。信号量限制对共享资源的并发访问次数。互斥锁序列化访问,因此并发用户必须等待。你想要哪个?信号灯。互斥锁应该由锁定它们的线程解锁。我需要线程等待其他人发布的信号量。。第一个想法:如果信号量计数由整数表示,则信号量上的原子减量(结果为负数)表示调用方需要等待。sema上导致零或负结果的原子增量表示存在需要释放的等待线程。在某些情况下,这会与保护线程等待的事件列表的“超级CS”一起使用更快的“无内核”路径生成更好的信号量吗?哦。。您是使用信号量与驱动程序通信,即受限系统调用,还是仅用于用户空间中的线程间通信?是的,使用信号量post块有点奇怪。但是考虑到键控事件的使用方式,只有当服务员正好在其快速路径(CAS)和对NtWaitForKeyedEvent的调用之间时才会发生这种情况,这应该是一个只有几个时钟周期的窗口