If performance is what you're after, and async at its finest level, I suggest looking into Completion Ports. This is what is ultimately underneath, hidden in the Windows Kernel, and it's awesome. When I used them, I used C++, even found a Kernel bug because of it, but I was limited to the language.
I've seen this article on CodeProject which might be worth exploring to see where you can take your idea further and/or use the code that's there.
The nature of Completion ports is to work on callbacks. That is, in general, you "put" a request in the queue, and when something lands there, the request is read and the callback specified is read. It is in fact, a queue, but like I said, at the lowest (manageable) level (before getting almost on metal).
EDIT: I've written a sort of a FTP server/client testing utility with Completion ports, so the base process is the same - reading and writing of commands in a queuable fashion. Hope it helps.
EDIT #2: Ok, here's what I would do, based on your feedback and comments. I would have an "outgoing queue", ConcurrentQueue<Message>
. You can have a separate thread for sending messages by dequeueing each message. Note, if you want it a bit more "safe", I suggest peeking at the message, sending it, then dequeuing it. Anyway, the Message class can be internal, and look something like this:
private class Message {
public string Command { get; set; }
... additonal properties, like timeouts, etc. ...
}
In the singleton class (I'll call it CommunicationService
), I'd also have a ConcurrentBag<Action<Response>>
. This is now where the fun starts :o). When a separate concern wants to do something, it registeres itself, for example, if you have a TemepratureMeter
I would have it do something like this:
public class TemperatureMeter {
private AutoResetEvent _signal = new AutoResetEvent(false);
public TemperatureMeter {
CommunicationService.AddHandler(HandlePotentialTemperatureResponse);
}
public bool HandlePotentialTemperatureResponse(Response response) {
// if response is what I'm looking for
_signal.Set();
// store the result in a queue or something =)
}
public decimal ReadTemperature() {
CommunicationService.SendCommand(Commands.ReadTemperature);
_signal.WaitOne(Commands.ReadTemperature.TimeOut); // or smth like this
return /* dequeued value from the handle potential temperature response */;
}
}
And now, in your CommunicationService, when you receive a response, you simply to a
foreach(var action in this._callbacks) {
action(rcvResponse);
}
Voila, separation of concerns. Does it answer your question any better?
Another possible tactic would be, to couple message and callback, but having the Callback be a Func<Response, bool>
and the dispatcher thread checks if the result returned from the Func is true, then this callback is disposed.