Мне нужно создать метод вызова, который может вызывать любой поток (например, поток 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 >> Beta.
Я могу использовать логические значения и еще много чего, но я никогда не получу настоящую атомную безопасность здесь. Я попробовал некоторые исправления, но они не сработали в случае, когда Beta >> Alpha.
Я также думал о блокировке методов Invoker.Execute и Invoker.Invoke, чтобы гарантировать, что выполнение не произойдет между постановкой в очередь и ожиданием. Однако проблема заключается в том, что блокировка также поглощает WaitOne и поэтому никогда не завершается (тупик).
Как мне добиться абсолютной атомной безопасности в этой парадигме?
Примечание: я действительно работаю с этим дизайном, исходя из внешних зависимостей. Таким образом, изменение дизайна не является реальным вариантом.
EDIT : я забыл упомянуть, что хочу блокировать поведение (на основе bool block
), пока делегат не будет выполнен при вызове Invoke.