How asynchronous methods work in C#?
I am using asynchronous methods in some of my project and I like it since it allows 开发者_运维技巧my application to be a lot more scalable. However, I am wondering how asynchronous methods really work in background? How .NET (or Windows?) knows that a call is completed? Depending on the number of asynchronous calls I made, I can see that new threads are created (not always though…). Why?
In addition, I would like to monitor how long a request take to complete. To test the concept, I wrote the following code which calls asynchronously a Web service and immediately after starts a stopwatch.
for (int i = 0; i < 10000; i++)
{
myWebService.BeginMyMethod(new MyRequest(), result, new AsyncCallback(callback), i);
stopWatches[i].Start();
}
// Call back stop the stopwatch after calling EndMyMethod
This doesn’t work since all the requests (10000) have the same begin time and the duration will go up linearly (call 0 = duration 1, call 1 = duration 2, etc.). How could I monitor the real duration of a call with asynchronous method (from the moment the request is really executed until the end)?
UPDATE: Does an asynchronous method block the flow? I understand that it uses the .NET ThreadPool
but how an IAsyncResult
know that a call is completed and it's time to call the CallBack
method?
The code is the railroad and the thread is the train. As train goes on railroad it executes the code.
BeginMyMethod
is executed by the main thread. If you look inside the BeginMyMethod
it simply adds a delegate of MyMethod
to the ThreadPool
's queue. The actual MyMethod
is executed by one of the trains of the train pool. The completion routine that is called when MyMethod
is done is executed by the same thread that executed the MyMethod
, not by your main thread that runs the rest of the code. While a thread pool thread is busy executing MyMethod
, the main thread can either ride some other portion of the railroad system (execute some other code), or simply sleep, waiting until certain semaphore is lit up.
Therefore there's no such thing as IAsyncResult
"knowing" when to call the completion routine, instead, completion routine is simply a delegate called by the thread pool's thread right after it's done executing MyMethod
.
I hope you don't mind the somewhat childish train analogy, I know it helped me more than once when explaining multithreading to people.
The crux of it is that calling Begin
queues up a request to execute your method. The method is actually executed on the ThreadPool, which is a set of worker threads provided by the runtime.
The threadpool is a fixed set of threads to crunch through async tasks as they get put into the queue. That explains why you see the execution time take longer and longer - your methods may each execute in approximately the same time, but they don't start until all previous methods in the queue have been executed.
To monitor the length of time it takes to actually execute the async method, you have to start and stop the timer at the beginning and end of your method.
Here's the docs for the ThreadPool class, and an article about async methods that do a better job of explaining what's going on.
Asynchronous methods work by using the .NET ThreadPool
. They will push the work onto a ThreadPool
thread (potentially creating one if needed, but usually just reusing one) in order to work in the background.
In your case, you can do what you're doing, however, realize that the ThreadPool
has a limited number of threads with which it will work. You're going to spawn your work onto background threads, and the first will run immediately, but after a while, they will queue up, and not work until "tasks" run before completely. This will give the appearance of the threads taking longer and longer.
However, your stopwatch criteria is somewhat flawed. You should measure the total time it takes to complete N tasks, not N times to complete one task. This will be a much more useful metric.
Its possible that a majority of the execution time happens before BeginMyMethod()
. In that case your measurement will be too low. In fact, depending on the API, BeginMyMethod()
may call the callback before leaving the stack itself. Moving up the call to StopWatch.Start()
should help then.
精彩评论