C# Multithreading – Can There Be Too Much Asynchronous Code?

cmultithreading

I am at the moment messing around with clients and servers in C# winforms and I'm trying to implement it all asynchronously. However, I'm beginning to wonder, should I use asynchronous code for everything?

Here's a list of what I'm doing asynchronously at the moment:

  1. TcpClient.BeginConnect with TcpClient.EndConnect

  2. NetworkStream.BeginRead with NetworkStream.EndRead

  3. TcpListener.BeginAcceptTcpClient with TcpListener.EndAcceptTcpClient

  4. Listening thread on server for client connections

  5. Listening thread on client for incoming data from server

  6. Listening Task on server for incoming data from each client

  7. New Task created every time an event such as ConnectionLost is raised (so the respective form can update). Think Delegate.BeginInvoke.

Everything is set up asynchrously and it works well, but I'm beginning to wonder if all of these should be asynchronous. I mean, it all sounds nice and people claim it to be efficient due to IO completion ports not blocking or something, but is it really?

I can understand having a single thread for listening on both the client and server, and for reading it makes sense as well. But every time an event is raised (which may be quite often!), should its invocation really be asynchronous? It seems like I am using Tasks for everything that can be made asynchronous and I'm not sure whether or not that is best practice.

Best Answer

Yes, it really is more efficient to do all this stuff asynchronously. If you do it synchronously then you're constantly polling, which wastes resources and can cause you to miss events if you don't process them quickly enough.

In addition, if you tried to do all that stuff synchronously, you'll find yourself mired in silly synchronization problems, trying to shuffle priorities, weird edge cases that come up when a client tries to connect within 22 msec of another client disconnecting, but only on Thursdays when there's a full moon.

With your current design, each logically separable task is separated from the rest of the program. It was likely easier to write, and it will be a whole lot easier to debug if something goes wrong. And maintenance is easier, too, because it's impossible (okay, very difficult) to get confused and modify the wrong thing. Having worked on systems like this that were not asynchronous, I can attest that it's incredibly easy to think you're diddling the input buffer when in fact you're mangling the output buffer.

As to your final question: yes, you really should raise every event asynchronously. Responding to an event can take an arbitrarily long amount of time. If you write it so that responding to the event is done synchronously, and that response takes a long time (which, in a communications app like this, could be five seconds or less), then you're going to miss events.

The asynchronous model you're using is very much like an implicit queue. It can tolerate spikes in traffic that would overwhelm a synchronous system. As long as, on average, you can process traffic faster than it comes in, you're fine. With a synchronous system, too many signals at once, or too many signals that take a long time to process, will kill the application. With the asynchronous model, the only way to kill the application is to overflow the queue.

Related Topic