7

我正在阅读http://msdn.microsoft.com/en-US/library/vstudio/hh191443.aspx。示例代码:

async Task<int> AccessTheWebAsync()
{ 
    // You need to add a reference to System.Net.Http to declare client.
    HttpClient client = new HttpClient();

    // GetStringAsync returns a Task<string>. That means that when you await the 
    // task you'll get a string (urlContents).
    Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");

    // You can do work here that doesn't rely on the string from GetStringAsync.
    DoIndependentWork();

    // The await operator suspends AccessTheWebAsync. 
    //  - AccessTheWebAsync can't continue until getStringTask is complete. 
    //  - Meanwhile, control returns to the caller of AccessTheWebAsync. 
    //  - Control resumes here when getStringTask is complete.  
    //  - The await operator then retrieves the string result from getStringTask. 
    string urlContents = await getStringTask;

    // The return statement specifies an integer result. 
    // Any methods that are awaiting AccessTheWebAsync retrieve the length value. 
    return urlContents.Length;
}

该页面还说:

async 和 await 关键字不会导致创建额外的线程。异步方法不需要多线程,因为异步方法不在自己的线程上运行

这个“没有创建额外的线程”是否适用于标记为异步的方法范围内?

我想为了让 GetStringAsync 和 AccessTheWebAsync 同时运行(否则 GetStringAsync 将永远无法完成,因为 AccessTheWebAsync 现在拥有控制权),最终 GetStringAsync 必须在与 AccessTheWebAsync 线程不同的线程上运行。

对我来说,编写异步方法仅在它等待的方法也是异步的时候不添加更多线程有用(它已经使用额外的线程并行地做自己的事情)

我的理解正确吗?

4

4 回答 4

14

这是力量的关键asyncGetStringAsync和其他自然异步操作不需要线程GetStringAsync只是发送 HTTP 请求并注册一个回调以在服务器回复时运行。不需要一个线程来等待服务器响应。

实际上,线程池只用了一点点。在上面的例子中,注册的回调GetStringAsync将在线程池线程上执行,但它所做的只是通知AccessTheWebAsync它可以继续执行。

我有一篇async介绍性的博客文章,您可能会觉得有帮助。

于 2013-07-18T22:04:52.613 回答
2

我想为了让 GetStringAsync 和 AccessTheWebAsync同时运行......

它们不会同时运行(至少不是你想的那样)。现在,如果 HttpClient.GetStringAsync 本身

  • 开始在不同的线程上工作,然后该代码可以同时运行,-或-
  • 如果有一个等待(使用 ConfigureAwait(false)),那么该方法中的其余工作将被安排到线程池线程上(这样代码就可以同时运行。

关键是,将方法声明为async和/或 usingawait不会导致您正在创作的方法在单独的线程上运行(您必须明确地这样做)。

注意:如果没有另一个异步方法的代码(或文档),你真的不知道它有多少是同步运行的。await直到该方法在另一个线程上执行或显式开始工作(通常通过启动 new Task),它才真正开始异步

于 2013-07-18T22:05:29.740 回答
0

如果有必要,可以从线程池中取出一个线程用于操作。在控制台的情况下(不讨论 UI 线程或 IO 线程),当前线程进入控制台,另一个线程被执行,这个新线程是执行剩余操作的线程。

于 2016-10-02T00:31:18.547 回答
-3

作者在这里所说的是,在某处简单地使用 async 和 await 关键字不会导致当前方法在不同的线程上运行。让我们调查提供的代码中发生了什么。

async Task<int> AccessTheWebAsync()
{ 
    // You need to add a reference to System.Net.Http to declare client.
    HttpClient client = new HttpClient();

    // GetStringAsync returns a Task<string>. That means that when you await the 
    // task you'll get a string (urlContents).
    Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");

到目前为止,一切都在一个线程中执行。此时,client.GetStringAsync 调用可以衍生出一个新线程(尽管这不是任何异步方法的保证)。应该注意的是,虽然 GetStringAsync 方法本质上是在当前线程上启动的(例如实际进行调用),但是一旦返回,读取响应将在不同的线程中执行。这由它返回一个 Task 对象来表示。Task 可以表示已经完成的工作,当前正在执行的辅助线程,或者只是计划稍后执行的工作单元。

DoIndependentWork();

这现在在我们的主线程上执行,在调用已排队(或可能发送)之后。因此,这可能在等待返回请求时发生。请注意,我们仍然在我们的主线程上。

 string urlContents = await getStringTask;

此时,我们的线程返回。.NET 将创建一个包含方法剩余部分的延续函数(返回 urlContents.Length;),一旦 getStringTask 在它自己的线程上完成,它将自动为我们调用。那时,一个新线程将接续。

通过所有这些,我们在 async 方法中编写的所有内容,直到 await 关键字,都在一个线程中运行。当然,我们调用了另一个方法,它可能碰巧产生另一个线程,但是我们没有做任何事情来创建另一个线程,只是使用 async/await 关键字。最后的延续可能由不同的线程调用,但是在我们的初始函数实际返回之后(这就是为什么我们的函数返回一个 Task 对象,以表示当对该函数的任何调用返回时可能尚未完成的工作) .

于 2013-07-18T22:08:23.033 回答