我需要创建一个方法调用程序,任何线程(例如,线程B)可以调用,它将在执行中的特定给定点的主执行线程(Thead 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&gt;&gt;那可以再现。贝塔。
我可以使用布尔值等等,但我在这里永远不会得到真正的原子安全。我尝试了一些修复,但是在Beta&gt;&gt;的情况下它们不起作用。阿尔法。
我还想过锁定Invoker.Execute和Invoker.Invoke方法,这样我们就可以保证在enqueing和waiting之间不会执行。但是,问题是锁也会使WaitOne失败,因此永远不会完成(死锁)。
我应该如何在这个范例中获得绝对的原子安全?
注意:我需要从外部依赖项开始使用此设计。因此,改变设计不是一个真正的选择。
编辑:我确实忘了提到我想要一个阻止行为(基于bool block
),直到在Invoke调用上执行委托。
答案 0 :(得分:1)
使用Semaphore(Slim)
代替ManualResetEvent
。
创建一个最大数量为1
的信号量,在调用线程中调用WaitOne()
,然后在代理中调用Release()
。
如果您已拨打Release()
,WaitOne()
应立即返回。
完成后请务必Dispose()
,最好是using
块
如果block
为false,则不应该首先创建它(尽管对于SemaphoreSlim
,这并不是那么糟糕。)
答案 1 :(得分:0)
您可以使用my technique:
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();
}
未测试
答案 2 :(得分:0)
我的回答只是为了展示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);
}
}
}