gpt4 book ai didi

winapi - 方法和停止功能之间同步的正确方法

转载 作者:行者123 更新时间:2023-12-02 08:21:35 25 4
gpt4 key购买 nike

我有一个函数(我们称之为函数 A),0 到多个线程可以访问它(同时,没有共享资源)。在任何给定时间,用户都可以用来停止该进程。停止功能需要确保有线程访问函数 A,以便可以执行正常关闭。是否有一个本地程序可以做到这一点?

我要做的是每次调用函数 A 时都有一个 InterlockedIncrement 一个整数(当函数 A 存在时,在所述整数上有一个相应的 InterlockedDecrement )。当发生 InterlockedDecrement 时,它会检查整数的值,如果将其设置为零,则将事件设置为有信号。如果该值不为零,则事件将设置为无信号。

这在我看来是有道理的,但我很好奇是否有更原生的结构/功能适合这样做。

我仍然需要考虑“停止”函数可能会匮乏的事实(从某种意义上说,所述整数可能永远不会设置为零)。旁注:当停止事件发生时,InterlockedIncrement 进程应停止,以减少所述饥饿。

最佳答案

您需要和想要的工具称为 Run-Down Protection 。不幸的是,它仅在内核模式下支持,但在用户模式下也很难自己实现。

最简单的实现如下:

HANDLE ghStopEvent;
LONG gLockCount = 1;
BOOLEAN bStop = FALSE;

void unlock()
{
if (!InterlockedDecrement(&gLockCount)) SetEvent(ghStopEvent);
}

BOOL lock()
{
LONG Value = gLockCount, NewValue;

for ( ; !bStop && Value; Value = NewValue)
{
NewValue = InterlockedCompareExchange(&gLockCount, Value + 1, Value);

if (NewValue == Value) return TRUE;
}

return FALSE;
}

void funcA();

void UseA()
{
if (lock())
{
funcA();
unlock();
}
}

当你想开始总结时 - 一旦调用

bStop = TRUE;解锁();

如何查看 lock 函数在 1 上互锁递增 gLockCount,但前提是它不为 0。

在内核模式下你可以调用

EX_RUNDOWN_REF gRunRef;

void UseA()
{
if (ExAcquireRundownProtection(&gRunRef))
{
funcA();
ExReleaseRundownProtection(&gRunRef)
}
}

并最终解锁 - ExWaitForRundownProtectionRelease

<小时/>

一些更复杂和可扩展的 rundown-protection 实现:

#define RUNDOWN_INIT_VALUE 0x80000000
#define RUNDOWN_COMPLETE_VALUE 0

class __declspec(novtable) RUNDOWN_REF
{
LONG _LockCount;

protected:

virtual void RundownCompleted() = 0;

public:

BOOL IsRundownBegin()
{
return 0 <= _LockCount;
}

void Reinit()
{
if (InterlockedCompareExchange(&_LockCount, RUNDOWN_INIT_VALUE, RUNDOWN_COMPLETE_VALUE) != RUNDOWN_COMPLETE_VALUE)
{
__debugbreak();
}
}

RUNDOWN_REF()
{
_LockCount = RUNDOWN_INIT_VALUE;
}

BOOL AcquireRundownProtection()
{
LONG Value = _LockCount, NewValue;

for ( ; Value < 0; Value = NewValue)
{
NewValue = InterlockedCompareExchange(&_LockCount, Value + 1, Value);

if (NewValue == Value) return TRUE;
}

return FALSE;
}

void ReleaseRundownProtection()
{
if (RUNDOWN_COMPLETE_VALUE == InterlockedDecrement(&_LockCount))
{
RundownCompleted();
}
}

void BeginRundown()
{
if (AcquireRundownProtection())
{
_interlockedbittestandreset(&_LockCount, 31);
ReleaseRundownProtection();
}
}
};

并像这样使用它:

class MY_RUNDOWN_REF : public RUNDOWN_REF
{
HANDLE _hEvent;

virtual void RundownCompleted()
{
SetEvent(_hEvent);
}
// ...
} gRunRef;


void UseA()
{
if (gRunRef.AcquireRundownProtection())
{
funcA();
gRunRef.ReleaseRundownProtection();
}
}

当你想停止时:

gRunRef.BeginRundown();// can be safe called multiple times
// wait on gRunRef._hEvent here
<小时/>

有趣的是,在内核中还存在其他一个(更旧的 - 从 win2000 开始,当来自 xp 的运行保护时)api Remove Locks 。它的作用几乎相同。仅在内部实现和使用方面有所不同。删除锁代码将如下所示:

IO_REMOVE_LOCK gLock;

void UseA()
{
if (0 <= IoAcquireRemoveLock(&gLock, 0))
{
funcA();
IoReleaseRemoveLock(&gLock, 0);
}
}

当我们想要停止时 - 调用

IoAcquireRemoveLock(&gLock, 0);
IoReleaseRemoveLockAndWait(&gLock, 0);

我的第一个代码 spinet 的实现接近删除锁实现,而第二个代码则接近 rundown-protection 实现。但从感觉上来说两者都是一样的

关于winapi - 方法和停止功能之间同步的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46898715/

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