I'm trying to determine the best way to approach this problem I am having with queuing outgoing messages to several COM ports. Each message is sent to the transmission queue of a static class that interfaces with multiple serial devices (Facade pattern). Each message is de-queued by a thread that is started upon the first established connection.
These messages can either request data from the serial device or simply write to an internal register. Either way, the handled message needs to relay back to the caller who originally submitted the message to the queue, the result of the IO operation (in order to process incoming data & update the UI).
So far I have attempted using three separate patterns, all of which I'm not sure the most appropriate.
1. Multiple listeners subscribed to events fired upon IO operation completion.
No queue involved. Callers simply invoke synchronous methods directly on the static class.
public void InstrumentCommandComplete(string aHwid, ResponseParameter aParameter, bool aConnState, object aResult)
{
OnInstrumentResponse?.Invoke(this, new InstrumentResponseArgs(aHwid, aParameter, aConnState, aResult));
}
This will fire each time a IO operation has completed and pass any relevant received data. Any interested classes simply subscribed to this event and handle any cross-thread operations accordingly.
I have found this method the easiest to implement and in my case, is the most readable. However, as the serial management class abstracts access to several devices, this requires multiple lock()
objects to avoid concurrent access to one port.
2. Command pattern with callbacks.
public interface ICommand
{
void Execute();
}
class ConnectCommand : ICommand
{
private readonly Func<bool, int> callback;
public ConnectCommand(Func<bool, int> aCallBack)
{
callback = aCallBack;
}
public void Execute()
{
callback(instrument.Connect());
}
}
In this case, commands are submitted to a queue in a static "CommandManager" and sequentially de-queued, processed and the caller informed via a provided callback delegate.
The issue here is that the callback method is a little more convoluted than my first attempt, as received data mostly consists of simple 16-bit integers.
3. Completion ports with callbacks.
Providing Asynchronous Serial Port Communication
Per the above question, I had attempted to implement Completion Ports. However I found that I required multiple AutoResetEvents
to handle the many callbacks that from the static serial manager class.
I did not have much success with this method and abandoned shortly after beginning refactoring as I needed to many signal variables to emulate synchronous calls to the ports.
I'm just having difficulty with finding the best implementation to handle requests to the device. At this point it seems most appropriate to:
a) either spawn new BackgroundWorkers for each request or...
b) submit each request to a ConcurrentQueue where each request contains a callback and is sequentially de-queued by a 'Manager'.
If anyone had thoughts on any alternate implementations, I would love to hear them.