有没有办法在没有锁的情况下同步它
Is there a way to synchronize this without locks?
假设我有 3 个可以由上层调用的函数:
-
Start
- 仅当我们尚未启动或之前调用 Stop 时才会调用 -
Stop
- 仅在成功调用"开始"屏幕后才会调用 -
Process
- 可以随时调用(同时在不同的线程上);如果启动,将调用到下层
在Stop
中,它必须等待所有Process
调用完成对下层的调用,并阻止任何进一步的调用。使用锁定机制,我可以想出以下伪代码:
Start() {
ResetEvent(&StopCompleteEvent);
IsStarted = true;
RefCount = 0;
}
Stop() {
AcquireLock();
IsStarted = false;
WaitForCompletionEvent = (RefCount != 0);
ReleaseLock();
if (WaitForCompletionEvent)
WaitForEvent(&StopCompleteEvent);
ASSERT(RefCount == 0);
}
Process() {
AcquireLock();
AddedRef = IsStarted;
if (AddedRef)
RefCount++;
ReleaseLock();
if (!AddedRef) return;
ProcessLowerLayer();
AcquireLock();
FireCompletionEvent = (--RefCount == 0);
ReleaseLock();
if (FilreCompletionEvent)
SetEvent(&StopCompleteEvent);
}
有没有办法在没有锁定机制的情况下实现相同的行为?也许有一些花哨的用法InterlockedCompareExchange和InterlockedIncremenet/InterlockedDecrement?
我问的原因是这是在网络驱动程序的数据路径中,我真的不希望有任何锁。
我相信可以
避免使用显式锁和任何不必要的阻塞或内核调用。
请注意,这只是伪代码,仅用于说明目的;它没有看到编译器。 虽然我相信线程逻辑是合理的,但请自己验证它的正确性,或者让专家来验证它;无锁编程很难。
#define STOPPING 0x20000000;
#define STOPPED 0x40000000;
volatile LONG s = STOPPED;
// state and count
// bit 30 set -> stopped
// bit 29 set -> stopping
// bits 0 through 28 -> thread count
Start()
{
KeClearEvent(&StopCompleteEvent);
LONG n = InterlockedExchange(&s, 0); // sets s to 0
if ((n & STOPPED) == 0)
bluescreen("Invalid call to Start()");
}
Stop()
{
LONG n = InterlockedCompareExchange(&s, STOPPED, 0);
if (n == 0)
{
// No calls to Process() were running so we could jump directly to stopped.
// Mission accomplished!
return;
}
LONG n = InterlockedOr(&s, STOPPING);
if ((n & STOPPED) != 0)
bluescreen("Stop called when already stopped");
if ((n & STOPPING) != 0)
bluescreen("Stop called when already stopping");
n = InterlockedCompareExchange(&s, STOPPED, STOPPING);
if (n == STOPPING)
{
// The last call to Process() exited before we set the STOPPING flag.
// Mission accomplished!
return;
}
// Now that STOPPING mode is set, and we know at least one call to Process
// is running, all we need do is wait for the event to be signaled.
KeWaitForSingleObject(...);
// The event is only ever signaled after a thread has successfully
// changed the state to STOPPED. Mission accomplished!
return;
}
Process()
{
LONG n = InterlockedCompareExchange(&s, STOPPED, STOPPING);
if (n == STOPPING)
{
// We've just stopped; let the call to Stop() complete.
KeSetEvent(&StopCompleteEvent);
return;
}
if ((n & STOPPED) != 0 || (n & STOPPING) != 0)
{
// Checking here avoids changing the state unnecessarily when
// we already know we can't enter the lower layer.
// It also ensures that the transition from STOPPING to STOPPED can't
// be delayed even if there are lots of threads making new calls to Process().
return;
}
n = InterlockedIncrement(&s);
if ((n & STOPPED) != 0)
{
// Turns out we've just stopped, so the call to Process() must be aborted.
// Explicitly set the state back to STOPPED, rather than decrementing it,
// in case Start() has been called. At least one thread will succeed.
InterlockedCompareExchange(&s, STOPPED, n);
return;
}
if ((n & STOPPING) == 0)
{
ProcessLowerLayer();
}
n = InterlockedDecrement(&s);
if ((n & STOPPED) != 0 || n == (STOPPED - 1))
bluescreen("Stopped during call to Process, shouldn't be possible!");
if (n != STOPPING)
return;
// Stop() has been called, and it looks like we're the last
// running call to Process() in which case we need to change the
// status to STOPPED and signal the call to Stop() to exit.
// However, another thread might have beaten us to it, so we must
// check again. The event MUST only be set once per call to Stop().
n = InterlockedCompareExchange(&s, STOPPED, STOPPING);
if (n == STOPPING)
{
// We've just stopped; let the call to Stop() complete.
KeSetEvent(&StopCompleteEvent);
}
return;
}
相关文章:
- 在没有太多条件句的情况下,我如何避免被零除
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 在未初始化映射的情况下,将值插入到映射的映射中
- 是默认情况下分配给char数组常量的值
- 为什么我不能在不创建字符串变量的情况下使用函数的字符串输出
- 如何在不产生任何垃圾的情况下获得C中的像素
- 在已经使用Git的情况下减少编译时间
- 为什么在Windows上的VS 2019和Clang 9中"size_t"在没有标题的情况下工作
- 如何在没有信号的情况下从C++执行QML插槽
- 松弛原子与无同步情况下的记忆连贯性
- 如何在没有同步的情况下使用多个线程(2、4,8、16 个线程)在循环(10,100、1000 个周期)中打印字符串?
- 多个线程/进程是否可以在不同步的情况下同时从/写入文件的非重叠区域?
- 如何在不同步的情况下创建线程本地内存
- 读取即将在没有同步的情况下同时增加的int是安全的吗?
- RAII 是否可以在不同步的情况下有效地在线程之间共享不可变对象
- 如何在不使用原子的情况下同步 CUDA 中的线程
- 有没有办法在考虑到崩溃的情况下同步进程
- 有没有办法在没有锁的情况下同步它
- 在不锁定文件的情况下同步更新文件
- 如果已知访问顺序是安全的,如何在没有互斥锁的情况下同步线程/CPU