Is it OK to do heavy processing in .NET's asynchronous callbacks, hogging them for multiple seconds before returning? Or am I depriving the OS / the runtime of important resources?
For example, consider TcpListener.BeginAcceptSocket
. My callback starts off by invoking EndAcceptSocket
, then spends a while receiving data, and only then closes the socket and returns. Is this how it was meant to be used, or am I expected to do the extra processing on my own thread?
Yes, this is how asynchronous Sockets (Tcp client, listeners, etc.) are designed for use. You should always ensure that you invoke the end aysnc method and then do whatever processing you desire. Not invoking the EndAccept(), EndSEnd(), EndReceive(), etc., etc. methods leaves you potentially open to a memory leak, so it's always a good practice to follow.
The threads that are used are no different than if you had manually spooled up a background thread yourself, and are infact desinged to be used for even "long term operations" of a couple of seconds. Trust me, you don't want anything that takes that long to be running on a dispatching or GUI thread.
I have over 90 mobile based systems which use asychronous Sockets for commucation to a Server and it does an excellent job: much faster than web services (remember all web protocols run on top of a Socket anyway), easy to detect errors, etc., etc.
I do the same on my Server code (mixed in with some other WCF for middleware and backend communication pieces) and it's the most scaleable comm that we've used. You'll have to do a Google on it but there's a guy who published his testing using this technology and he was able to support 1,000 concurrent communications to a server with just 11 threads. Not bad.
Server example from MS: http://msdn.microsoft.com/en-us/library/fx6588te.aspx
Client example from MS: http://msdn.microsoft.com/en-us/library/bew39x2a.aspx
It takes more than these to get it "perfected" but this is a good place to start.
I repeated the experiments of a CodeProject article on this topic and found the results for .NET 4 to be similar to what were described in 2003. Note that the article did not actually list the results for the problematic section but as I understand it the main issue still exists.
I reused the code from the CodeProject article - just download it to run this test yourself or to experiment.
The test will try to use 10 parallel threads to count as high as it can in 1 second.
Using 10 background threads (i.e. new Thread()
)
T0 = 4451756 T1 = 4215159 T2 = 5449189 T3 = 6244135 T4 = 3297895 T5 = 5302370 T6 = 5256763 T7 = 3779166 T8 = 6309599 T9 = 6236041 Total = 50542073
Using 10 ThreadPool work items
T0 = 23335890 T1 = 20998989 T2 = 22920781 T3 = 9802624 T4 = 0 T5 = 0 T6 = 0 T7 = 0 T8 = 0 T9 = 0 Total = 77058284
Note that only 4 thread pool work items out of 10 ever actually executed during the 1 second time slice! This is on a quad-core CPU, so that was one thread per core. The other tasks executed after the first four completed and because the 1 second allotted had already expired, they did not increment their counters.
The conclusion here: with long tasks, ThreadPool will make some tasks wait behind others! Thus, I would strongly recommend against doing any long processing in ThreadPool tasks (such as asynchronous completion handlers). Otherwise, you might keep more important asynchronous calls from completing if your data processing is hogging the CPU, or you might have very unstable performance if only some tasks do much processing.
Using custom ThreadPool implementation from article
T0 = 7175934 T1 = 6983639 T2 = 5306292 T3 = 5078502 T4 = 3279956 T5 = 8116320 T6 = 3262403 T7 = 7678457 T8 = 8946761 T9 = 8500619 Total = 64328883
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With