gpt4 book ai didi

c++ - 产品/消费者——什么是最佳信号模式

转载 作者:可可西里 更新时间:2023-11-01 14:13:51 25 4
gpt4 key购买 nike

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

void wake_thread(thread)

void sleep_thread(thread)

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

简单的方法当然是使用这样的同步事件:

hEvent = CreateEvent(NULL, FALSE, TRUE, NULL);

void wake_thread(thread) {

SetEvent(hEvent);
}

和:

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

这提供了所需的语义并且没有场景的竞争条件(只有一个线程在等待,但多个线程可以发出信号)。我把它放在这里是为了展示我想要调整的内容。

但是,我想知道在 Windows 下对于这种非常特殊的情况是否有更快的方法。 wake_thread 可能会被调用很多次,即使 C 没有休眠。这会导致对 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();
}

基本上,这使用手动重置事件和两位标志来重现自动重置事件的行为。画个状态图可能会更清楚。线程安全取决于允许哪些函数进行哪些转换的规则,以及何时允许向事件对象发出信号。

作为社论:我认为将同步代码分离到 wake_thread 和 sleep_thread 函数中会让事情变得有点尴尬。如果将同步代码移到队列实现中,它可能会更自然、更高效,而且几乎肯定会更清晰。

关于c++ - 产品/消费者——什么是最佳信号模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14128380/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com