我正在构建一个需要两个函数来同步线程的高性能应用程序

void wake_thread(thread)

void sleep_thread(thread)

该应用程序具有一个线程(可以称为C),可以通过调用sleep_thread进入休眠状态。有多个线程将调用wake_thread。当ake_thread返回时,它必须保证C正在运行或将被唤醒。 wake_thread绝不能阻塞。

当然,最简单的方法是使用如下所示的同步事件:
hEvent = CreateEvent(NULL, FALSE, TRUE, NULL);

void wake_thread(thread) {

  SetEvent(hEvent);
}

和:
void sleep_thread(thread)
{
  WaitForSingleObject(hEvent);
}

这提供了所需的语义,并且没有场景的竞争条件(只有一个线程在等待,但有多个线程可以发出信号)。我将其包含在此处以显示我要调整的内容。

但是,我想知道在Windows下对于这种非常特定的情况是否有更快的方法。即使C不处于 sleep 状态,也可以多次调用ake_thread 。这将导致对SetEvent的大量调用,但无济于事。有没有一种更快的方法来使用手动重置事件和引用计数器来确保仅在实际需要设置时才调用SetEvent。

在这种情况下,每个CPU周期都很重要。

最佳答案

我没有测试过(除了确保它可以编译),但我认为这应该可以解决问题。诚然,这比我一开始想的要棘手。请注意,您可以进行一些明显的优化。为了清楚起见,我将其保留为未优化的形式,以帮助进行必要的调试。我也省略了错误检查。

#include <intrin.h>

HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
__declspec(align(4)) volatile LONG thread_state = 2;
    // 0 (00): sleeping
    // 1 (01): sleeping, wake request pending
    // 2 (10): awake, no additional wake request received
    // 3 (11): awake, at least one additional wake request

void wake_thread(void)
{
    LONG old_state;

    old_state = _InterlockedOr(&thread_state, 1);
    if (old_state == 0)
    {
        // This is the first wake request since the consumer thread
        // went to sleep.  Set the event.

        SetEvent(hEvent);
        return;
    }
    if (old_state == 1)
    {
        // The consumer thread is already in the process of being woken up.
        // Any items added to the queue by this thread will be processed,
        // so we don't need to do anything.

        return;
    }
    if (old_state == 2)
    {
        // This is an additional wake request when the consumer thread
        // is already awake.  We've already changed the state accordingly,
        // so we don't need to do anything else.

        return;
    }
    if (old_state == 3)
    {
        // The consumer thread is already awake, and already has an
        // additional wake request registered, so we don't need to do
        // anything.

        return;
    }
    BigTrouble();
}

void sleep_thread(void)
{
    LONG old_state;

    // Debugging only, remove this test in production code.
    // The event should never be signaled at this point.

    if (WaitForSingleObject(hEvent, 0) != WAIT_TIMEOUT)
    {
        BigTrouble();
    }

    old_state = _InterlockedAnd(&thread_state, 1);
    if (old_state == 2)
    {
        // We've changed the state from "awake" to "asleep".
        // Go to sleep.

        WaitForSingleObject(hEvent, INFINITE);

        // We've been buzzed; change the state to "awake"
        // and then reset the event.

        if (_InterlockedExchange(&thread_state, 2) != 1)
        {
            BigTrouble();
        }
        ResetEvent(hEvent);
        return;
    }
    if (old_state == 3)
    {
        // We've changed the state from "awake with additional
        // wake request" to "waking".  Change it to "awake"
        // and then carry on.

        if (_InterlockedExchange(&thread_state, 2) != 1)
        {
            BigTrouble();
        }
        return;
    }
    BigTrouble();
}

基本上,这使用手动重置事件和两位标志来重现自动重置事件的行为。如果绘制状态图,可能会更清楚。线程安全性取决于关于允许哪个函数进行哪些转换的规则,还取决于何时允许用信号通知事件对象。

作为社论:我认为它将同步代码分为ake_thread和sleep_thread函数,这使事情有些尴尬。如果将同步代码移入队列实现中,则可能会更自然,效率更高并且几乎可以肯定地更加清晰。

10-04 11:07