Does C# AsyncCallback creates a new thread? Does C# AsyncCallback creates a new thread? multithreading multithreading

Does C# AsyncCallback creates a new thread?


It is entirely an implementation detail of the class' BeginXxx() method. There are two basic schemes:

  • BeginXxx() starts a thread to get the job done, that thread makes the callback
  • BeginXxx() asks the operating system to get job done, using an I/O completion port to ask to be notified when it is done. The OS starts a thread to deliver the notification, that runs the callback.

The second approach is very desirable, it scales well with the program being able to have many pending operations. And is the approach used by HttpListener, the TCP/IP driver stack on Windows supports completion ports. Your program can support thousands of sockets easily, important in server scenarios.

The EndXxx() call in the callback reports any mishaps encountered while trying to complete the I/O request by throwing an exception. In your case, the BeginGetContext() requires EndGetContext() in the callback. If you don't catch the exception then your program will terminate.

Your code snippet does not actually demonstrate any asynchronous I/O. You called GetResponse() instead of BeginGetResponse(). No callback is involved at all, it will thus be the GetResponse() method that fails and throws the exception.


Does C# AsyncCallback creates a new thread?

No, it does not itself, it is just a callback. However, the code which invokes the callback may be calling it on a thread different from the thread the original operation was started on (in your case, the operation is httpListener.BeginGetContext).

Usually (but not necessary), it is invoked on a random ThreadPool thread which happened to handle the completion of the underlying socket IO operation (for more details, here is a great read: There Is No Thread).

Use an AsyncCallback delegate to process the results of an asynchronous operation in a separate thread.

I believe that means you should process the results of an asynchronous operation on the thread your callback is called on. This thread is almost always separate from the thread which initiated the operation, as explained above. That's also what their sample code apparently does. Note they don't create any new threads inside ProcessDnsInformation.

Once you have received the callback, it is up to you then how to organize the threading model of your server application. The main concern is, it should remain being responsive and scalable. Ideally, you should serve the incoming request on the same thread it arrives on, and release this thread as soon as you've done with any CPU-bound job required to process the request. As a part of the processing logic, you may need to do other IO-bound tasks (access files, execute DB queries, call web services, etc). While doing that, you should be using asynchronous versions of the relevant APIs as mush as possible, to avoid blocking the request thread (again, refer to There Is No Thread).

IMO, with the Asynchronous Programming Model (APM) pattern that you've chosen, implementing such logic might be quite a tedious task (especially, the error handling and recovery part of it).

However, using Task Parallel Library (TPL), async/await pattern and Task-based APIs like HttpListener.GetContextAsync, it's a piece of cake. The best part: you don't have to worry about threading much any more.

To give you an idea of what I'm talking about, here is an example of a low-level TCP server. A very similar concept can be used while implementing an HttpListener-based HTTP server.


To add to Hans' excellent answer,

Even when an asynchronous operation is performed, it is actually possible for it to complete synchronously -- even in scheme #2, it is incorrect to say callbacks will come back on another thread. Counting on that behavior might actually end up with you getting deadlocks or overflowing your stack.

You can check the IAsyncResult.CompletedSynchronously property to determine this. When set to true, it is highly likely that the completion callback came in on the same thread that started the async operation: the callback will run during your Begin* call, and must complete before it can return.