跨线程同步/原子检查
本文关键字:检查 线程 同步 | 更新日期: 2023-09-27 18:02:36
我需要创建一个任何线程(例如线程B)都可以调用的方法调用器,它将在执行的特定给定点在主执行线程(head A)上执行。
用法示例如下:
static Invoker Invoker = new Invoker();
static void ThreadA()
{
new Thread(ThreadB).Start();
Thread.Sleep(...); // Hypothetic Alpha
Invoker.Invoke(delegate { Console.WriteLine("Action"); }, true);
Console.WriteLine("Done");
Console.ReadLine();
}
static void ThreadB()
{
Thread.Sleep(...); // Hypothetic Beta
Invoker.Execute();
}
Invoker类是这样的:
public class Invoker
{
private Queue<Action> Actions { get; set; }
public Invoker()
{
this.Actions = new Queue<Action>();
}
public void Execute()
{
while (this.Actions.Count > 0)
{
this.Actions.Dequeue()();
}
}
public void Invoke(Action action, bool block = true)
{
ManualResetEvent done = new ManualResetEvent(!block);
this.Actions.Enqueue(delegate
{
action();
if (block) done.Set();
});
if (block)
{
done.WaitOne();
}
}
}
这在大多数情况下工作得很好,但如果由于某种原因,执行(因此Set
)在WaitOne
之前完成,则不会,在这种情况下,它将冻结(它允许线程继续,然后阻塞)。如果Alpha>> Beta,可以复制。
我可以使用布尔值之类的,但我从来没有得到真正的原子安全性。我尝试了一些修复,但是它们在Beta>> Alpha的情况下不起作用。
我还想过锁定两个调用者。执行和调用。调用方法,这样我们就可以保证执行不会发生在队列和等待之间。然而,问题是锁也包含WaitOne,因此永远不会结束(死锁)。
在这个范例中,我应该如何获得绝对的原子安全?
注意:这真的是一个要求,我与这个设计工作,从外部依赖。所以改变设计不是一个真正的选择。
EDIT:我忘记提到我想要一个阻塞行为(基于bool block
),直到委托在调用上执行。
用Semaphore(Slim)
代替ManualResetEvent
。
创建一个最大计数为1
的信号量,在调用线程中调用WaitOne()
,在委托中调用Release()
。
如果你已经调用了Release()
, WaitOne()
应该立即返回。
当你完成时,确保Dispose()
,最好是在using
块。
如果block
为false,那么首先就不应该创建它(尽管对于SemaphoreSlim
来说,这并不是那么糟糕)。
你可以用我的方法:
public void BlockingInvoke(Action action)
{
volatile bool isCompleted = false;
volatile bool isWaiting = false;
ManualResetEventSlim waiter = new ManualResetEventSlim();
this.Actions.Enqueue(delegate
{
action();
isCompleted = true;
Thread.MemoryBarrier();
if (!isWaiting)
waiter.Dispose();
else
waiter.Set();
});
isWaiting = true;
Thread.MemoryBarrier();
if (!isCompleted)
waiter.Wait();
waiter.Dispose();
}
未经考验的
我回答这个问题只是为了展示SLaks描述的实现和我的解决方案,以确保锁的正确和唯一处理。它可以接受改进和批评,但它确实有效。
public class Invoker
{
private Queue<Action> Actions { get; set; }
public Invoker()
{
this.Actions = new Queue<Action>();
}
public void Execute()
{
while (this.Actions.Count > 0)
{
this.Actions.Dequeue()();
}
}
public void Invoke(Action action, bool block = true)
{
if (block)
{
SemaphoreSlim semaphore = new SemaphoreSlim(1);
bool disposed = false;
this.Actions.Enqueue(delegate
{
action();
semaphore.Release();
lock (semaphore)
{
semaphore.Dispose();
disposed = true;
}
});
lock (semaphore)
{
if (!disposed)
{
semaphore.Wait();
semaphore.Dispose();
}
}
}
else
{
this.Actions.Enqueue(action);
}
}
}