等待在这个功能中做什么?

der*_*khh 2 c# asynchronous async-await

我以为我理解了这个async-await模式,C#但今天我发现我真的没有.

在这样一个简单的代码片段中.我已经System.Net.ServicePointManager.DefaultConnectionLimit = 1000;定义了.

public static async Task Test()
{
    HttpClient client = new HttpClient();
    string str;
    for (int i = 1000; i <= 1100; i++)
        str = await client.GetStringAsync($"https://en.wikipedia.org/wiki/{i1}");
}
Run Code Online (Sandbox Code Playgroud)

是什么await在这里做?最初我认为因为这是在async-await模式中,这意味着基本上HttpClient将以多线程方式启动所有HTTP GET调用,即基本上应该立即获取所有Url.

但是,当我使用Fiddler来分析我发现的行为时,它确实按顺序获取了URL.

我需要将其更改为此以使其工作:

public static async Task<int> Test()
{
    int ret = 0;
    HttpClient client = new HttpClient();
    List<Task> taskList = new List<Task>();
    for (int i = 1000; i <= 1100; i++)
    {
        var i1 = i;
        var task = Task.Run(() => client.GetStringAsync($"https://en.wikipedia.org/wiki/{i1}"));
        taskList.Add(task);
    }
    Task.WaitAll(taskList.ToArray());
    return ret;
}
Run Code Online (Sandbox Code Playgroud)

这次是并行获取URL.那么await关键字在第一个代码片段中真正做了什么?

Ami*_*mit 11

等待 HTTP请求的完成.for只有在每个请求完成后,代码才会恢复(迭代...).

您的第二个版本正常工作,因为在启动以下任务之前,它不等待每个任务完成,并且仅在所有任务完成后等待所有任务完成.

async-await非常有用,它允许调用函数在异步函数等待时继续执行其他操作,而不是同步("正常")函数阻塞调用函数直到完成.


Gar*_*all 8

An await异步等待。这不是阻塞调用,它允许您的方法的调用者继续。返回值完成后,await将执行方法之后的方法内的其余代码Task

在代码的第一个版本中,允许调用者继续。但是,循环的每次迭代都将等待,直到Task返回GetStringAsync完成为止。这具有顺序下载每个URL 的效果,而不是同时下载。

请注意,代码的第二个版本不是异步的,因为它使用线程并行执行工作。

如果是异步的,它将仅使用一个线程检索网页内容,但仍同时进行

像这样(未经测试):

public static async Task<int> Test()
{
    int ret = 0;
    HttpClient client = new HttpClient();
    List<Task> taskList = new List<Task>();
    for (int i = 1000; i <= 1100; i++)
    {
        var i1 = i;
        taskList.Add(client.GetStringAsync($"https://en.wikipedia.org/wiki/{i1}"));
    }
    await Task.WhenAll(taskList.ToArray());
    return ret;
}
Run Code Online (Sandbox Code Playgroud)

在这里,我们异步启动任务并将其添加到中taskList。这些任务是非阻塞性的,将在下载完成并检索到字符串后完成。注意调用Task.WhenAll而不是Task.WaitAll:前者是异步和非阻塞的,后者是同步和阻塞的。这意味着,在处await,此Test()方法的调用者将收到Task<int>返回的内容:但是,直到下载了所有字符串后,任务才能完成。

这就是迫使async/ await在整个堆栈中扩散的原因。一旦最底层的调用是异步的,则只有所有其他调用者也都是异步的才有意义。否则,您将被迫通过Task.Run()调用等方法创建线程。