异步方法如何在C#中工作?

时间:2009-10-24 00:12:03

标签: c# .net-3.5 asynchronous

我在我的一些项目中使用异步方法,我喜欢它,因为它允许我的应用程序更具可伸缩性。但是,我想知道异步方法在后台如何真正起作用? .NET(或Windows?)如何知道呼叫已完成?根据我所做的异步调用的数量,我可以看到创建了新线程(但并不总是......)。为什么呢?

此外,我想监控请求完成的时间。为了测试这个概念,我编写了以下代码,它在启动秒表后立即异步调用Web服务。

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

这不起作用,因为所有请求(10000)具有相同的开始时间并且持续时间将线性上升(调用0 =持续时间1,调用1 =持续时间2等)。如何使用异步方法监控调用的实际持续时间(从请求真正执行到结束的那一刻)?


UPDATE :异步方法是否会阻止流量?我了解它使用.NET ThreadPool,但IAsyncResult如何知道调用已完成并且是时候调用CallBack方法了?

4 个答案:

答案 0 :(得分:3)

代码是铁路,线程是火车。当火车进入铁路时,它会执行代码。

BeginMyMethod由主线程执行。如果您查看BeginMyMethod内部,只需将MyMethod的委托添加到ThreadPool的队列中。实际的MyMethod由列车的一列火车执行。完成MyMethod时调用的完成例程由执行MyMethod的同一线程执行,而不是由运行其余代码的主线程执行。当一个线程池线程忙于执行MyMethod时,主线程可以乘坐铁路系统的其他部分(执行一些其他代码),或者只是睡觉,等待直到某些信号量被点亮。

因此,没有IAsyncResult“知道”什么时候调用完成例程,相反,完成例程只是线程池的线程在执行MyMethod之后立即调用的委托。 / p>

我希望你不介意有些幼稚的火车比喻,我知道在向人们解释多线程时,它不止一次帮助过我。

答案 1 :(得分:1)

它的关键在于调用Begin将执行方法的请求排队。该方法实际上是在ThreadPool上执行的,ThreadPool是运行时提供的一组工作线程。

线程池是一组固定的线程,用于在异步任务进入队列时处理它们。这就解释了为什么你看到执行时间越来越长 - 你的方法可能会在大致相同的时间内执行,但是在队列中所有先前的方法都被执行之前它们才会启动。

要监视实际执行异步方法所需的时间长度,您必须在方法的开头和结尾处启动和停止计时器。

以下是ThreadPool课程的文档,以及有关async methods的文章,可以更好地解释正在发生的事情。

答案 2 :(得分:1)

异步方法使用.NET ThreadPool。他们会将工作推送到ThreadPool线程(如果需要,可能会创建一个,但通常只重用一个),以便在后台工作。

在你的情况下,你可以做你正在做的事情,然而,意识到ThreadPool具有有限数量的线程,它将用于它。你将把你的工作产生到后台线程上,第一个会立即运行,但过了一段时间,它们会排队,直到“任务”完全运行之后才能工作。这将使线程的外观花费更长时间。

但是,你的秒表标准有些缺陷。您应该测量完成N个任务所需的总时间,而不是完成一个任务的N次。这将是一个更有用的指标。​​

答案 3 :(得分:0)

大部分执行时间可能发生在BeginMyMethod()之前。在这种情况下,您的测量结果会太低。实际上,根据API,BeginMyMethod()可能会在离开堆栈之前调用回调。将呼叫提升到StopWatch.Start()应该会有所帮助。