方法和停止功能之间的正确同步方式
Correct way of synchronization between a method and a stop functionality
我有一个函数(我们称之为函数 A),0 到许多线程可以访问它(同时,没有共享资源)。在任何给定时间,用户都可以使用停止进程。停止功能需要确保有线程访问函数 A,以便可以执行正常关闭。有这样做的本机程序吗?
我要做的是在每次调用函数 A 时都有一个 InterlockedIncrement 整数(当函数 A 存在时,在所述整数上有一个相应的 InterlockedDecrement)。当 InterlockedDecrement 发生时,它检查整数的值,如果它设置为零,则事件设置为发出信号。如果该值不为零,则事件设置为未发出信号。
这在我看来是有道理的,但我很好奇是否有更适合这样做的原生结构/功能。
我仍然不得不考虑 "stop" 函数可能会被饿死的事实(从某种意义上说,所述整数可能永远不会设置为零)。旁注:当停止事件发生时,应停止 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; unlock();
你怎么看 lock
函数是在 1 上互锁递增 gLockCount
但前提是它不是 0。
在内核模式下你可以调用
EX_RUNDOWN_REF gRunRef;
void UseA()
{
if (ExAcquireRundownProtection(&gRunRef))
{
funcA();
ExReleaseRundownProtection(&gRunRef)
}
}
并进入决赛 unlock
- ExWaitForRundownProtectionRelease
一些更复杂和可扩展的破损保护实现:
#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,当 rundown protection 来自 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 通过实现接近删除锁实现,当第二个接近破损保护实现时。但从感觉上来说两者都是一样的
我有一个函数(我们称之为函数 A),0 到许多线程可以访问它(同时,没有共享资源)。在任何给定时间,用户都可以使用停止进程。停止功能需要确保有线程访问函数 A,以便可以执行正常关闭。有这样做的本机程序吗?
我要做的是在每次调用函数 A 时都有一个 InterlockedIncrement 整数(当函数 A 存在时,在所述整数上有一个相应的 InterlockedDecrement)。当 InterlockedDecrement 发生时,它检查整数的值,如果它设置为零,则事件设置为发出信号。如果该值不为零,则事件设置为未发出信号。
这在我看来是有道理的,但我很好奇是否有更适合这样做的原生结构/功能。
我仍然不得不考虑 "stop" 函数可能会被饿死的事实(从某种意义上说,所述整数可能永远不会设置为零)。旁注:当停止事件发生时,应停止 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; unlock();
你怎么看 lock
函数是在 1 上互锁递增 gLockCount
但前提是它不是 0。
在内核模式下你可以调用
EX_RUNDOWN_REF gRunRef;
void UseA()
{
if (ExAcquireRundownProtection(&gRunRef))
{
funcA();
ExReleaseRundownProtection(&gRunRef)
}
}
并进入决赛 unlock
- ExWaitForRundownProtectionRelease
一些更复杂和可扩展的破损保护实现:
#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,当 rundown protection 来自 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 通过实现接近删除锁实现,当第二个接近破损保护实现时。但从感觉上来说两者都是一样的