I know that similar things exist in WPF and forms applications with the Control.Invoke method, I also know of the existence of BackgroundWorker, ThreadPool etc.
However, I don't want to depend on Forms/WPF, and I want to make sure work is executed sequentially and on one thread.
Edit: Rationale: I want to drive a state machine from one thread. The events come from other threads tough. There is no UI.
So far I couldn't really figure out how to do this with existing framework classes but I might have misunderstood the documentation.
Edit: I forgot to mention I'm bound to .NET Framework 3.5
What I wrote so far:
public class Dispatcher
{
string Name;
Thread WorkerThread;
Queue<Action> WorkQueue;
List<Exception> Exceptions;
ManualResetEvent Gate;
volatile bool KeepRunning;
readonly object WorkLocker;
public override string ToString()
{
return String.Format("{0}({1})", this.GetType().Name, Name);
}
public Dispatcher(string name)
{
Name = name;
WorkLocker = new Object();
Gate = new ManualResetEvent(false);
WorkQueue = new Queue<Action>();
Exceptions = new List<Exception>();
}
public void Start()
{
if (WorkerThread == null)
{
WorkerThread = new Thread(doDispatch)
{
IsBackground = true,
Name = this.Name
};
WorkerThread.Start();
}
}
public void Stop()
{
if (WorkerThread != null && WorkerThread.IsAlive)
{
Dispatch(() => { KeepRunning = false; });
WorkerThread.Join();
}
WorkerThread = null;
}
public void Reset()
{
Stop();
lock (WorkLocker)
{
WorkQueue = new Queue<Action>();
Exceptions = new List<Exception>();
}
}
public void Dispatch(Action a)
{
lock (WorkLocker)
{
WorkQueue.Enqueue(a);
}
Gate.Set();
}
public List<Exception> CollectExceptions()
{
List<Exception> result = new List<Exception>();
lock(WorkLocker)
{
foreach(Exception e in Exceptions)
{
result.Add(e);
}
Exceptions.Clear();
}
return result;
}
private void doDispatch()
{
KeepRunning = true;
while (KeepRunning)
{
Gate.WaitOne();
lock (WorkLocker)
{
while (WorkQueue.Count > 0)
{
try
{
WorkQueue.Dequeue()?.Invoke();
}
catch (Exception e)
{
Exceptions.Add(e);
}
}
}
}
}
}
Is there a way to do something like this in a simpler way? Another nice feature would be being able to dispatch calls that have multiple arguments.
Since you are bound to 3.5 you can't use BlockingCollection or the DataFlow library...you'll have to roll your own implementation.
The sample code you provided is a good start, but you should apply the Single Responsibility Principle to make it cleaner and easier to refactor when(if?) you upgrade the .NET Framework.
I would do it like this: