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非常有用,它允许调用函数在异步函数等待时继续执行其他操作,而不是同步("正常")函数阻塞调用函数直到完成.
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()调用等方法创建线程。