C# 异步方法在后台做什么?
我已经阅读了关于AsyncAwait的各种文章,我正在尝试深入理解await异步。我的问题是,我发现等待一个异步方法并不会创建一个新线程,它只会使UI响应。如果是这样的话,在使用wait-async时就没有时间增益,因为没有使用额外的线程 到目前为止,我只知道Task.Run()创建一个新线程。 对于Task.WhenAll()或Task.WhenAny()也是这样吗 假设我们有以下代码:C# 异步方法在后台做什么?,c#,asynchronous,async-await,threadpool,C#,Asynchronous,Async Await,Threadpool,我已经阅读了关于AsyncAwait的各种文章,我正在尝试深入理解await异步。我的问题是,我发现等待一个异步方法并不会创建一个新线程,它只会使UI响应。如果是这样的话,在使用wait-async时就没有时间增益,因为没有使用额外的线程 到目前为止,我只知道Task.Run()创建一个新线程。 对于Task.WhenAll()或Task.WhenAny()也是这样吗 假设我们有以下代码: async Task<int> AccessTheWebAsync()
async Task<int> AccessTheWebAsync()
{
using (HttpClient client = new HttpClient())
{
Task<string> getStringTask = client.GetStringAsync("https://docs.microsoft.com");
DoIndependentWork();
string urlContents = await getStringTask;
return urlContents.Length;
}
}
async任务访问webasync()
{
使用(HttpClient=new HttpClient())
{
任务getStringTask=client.GetStringAsync(“https://docs.microsoft.com");
做独立的工作();
字符串urlContents=等待getStringTask;
返回urlContents.Length;
}
}
我的期望:
所以我真的不明白在等待任务时如何不创建额外的线程。有人能解释一下等待任务时到底发生了什么吗?你的基本假设—任务总是在线程上运行—确实是错误的。一个简单的反例是一个完全不运行的基于计时器的任务:它只是订阅计时器,并在计时器触发时将任务状态设置为“已完成” 更有用、更实用的任务实例是不在任何地方运行的任务—网络请求:它们发送请求,订阅传入的应答,然后停止运行,释放线程以进行另一项工作* 所以让我们考虑一下你的实际问题。
到目前为止,我只知道Task.Run()创建一个新线程。对于Task.WhenAll()或Task.WhenAny()也是这样吗 否,
Task。whalll
不会创建任何新线程。它将等待已经存在的任务完成,而不管它们在哪里运行(也不管它们是否在任何线程中运行!)
由task.whalll
创建的任务未在任何特定线程中运行!它只检测底层任务何时完成,在所有任务都准备好之后,它自己也会完成<代码>任务。whalll不需要任何线程来执行此操作
创建getStringTask任务时,另一个线程将复制当前上下文并开始执行GetStringAsync方法 正如我们前面所看到的,调用像
GetStringAsync
这样的异步方法不会在任何特定线程上执行。GetStringAsync
的代码设置这些内容,以便在得到答案时,它能够重新获得控制权(可能是在线程池线程上),并将控制权交还给您。准备工作可以在当前线程上完美完成,不会花费太多时间*
*免责声明:这是一种简化,事实上,网络异步请求执行的操作顺序要复杂得多。一篇帮助我理解async await的文章是,他将async await与厨师做早餐进行了比较。在中间搜索某处,等待。< /P> 如果一个厨师不得不做早餐,他只是在烤面包机里放了一些面包,他不会等面包烤好,而是开始环顾四周,看看他是否能做些别的事情,比如煮开水泡茶 当看到async等待时,也会发生类似的情况。如果您调用一个异步函数,您就知道其中的某个地方有一个wait。事实上,如果您忘记在异步函数中等待,编译器将警告您 一旦线程看到等待,它就不会无所事事地等待等待任务完成,而是四处查看是否可以做其他事情。它可以进入调用堆栈,查看是否有一个调用方尚未等待,并执行这些语句,直到看到等待为止。再次进入调用堆栈并执行语句,直到看到等待 无法保证在未等待的异步调用后继续执行语句的线程与原始线程相同。但是,由于该线程具有相同的“上下文”,因此可以将其视为同一线程。不需要临界截面等
Console.Writeline(Thread.CurrentThread.ManagedThreadId);
// async call to the text reader to read a line; don't await
var taskReadLine = myTextReader.ReadLineAsync()
// because I did not await, the following will be executed as soon as a thread is free
Console.Writeline(Thread.CurrentThread.ManagedThreadId);
...
// we need the read line; await for it
string readLine = await taskReadLine;
Console.Writeline(Thread.CurrentThread.ManagedThreadId);
ProcessReadLine(readLine);
无法保证执行DoSomething的线程与用于调用ReadLineAsync的线程相同。如果您在一个简单的测试程序中执行代码,很可能会获得多个线程id
在等待结果之前,代码不应依赖于要执行的异步函数中的任何语句:
async Task<int> DoIt()
{
this.X = 4;
await DoSomethingElseAsync(this.X);
return 5;
}
async Task CallDoItAsync()
{
this.X = 0;
var taskDoIt = DoIt();
// you didn't await, it is not guaranteed that this.X already changed to 4
...
int i = await taskDoIt();
// now you can be certain that at some moment 4 had been assigned to this.X
更改线程池时要非常小心
有人说async await只在保持UI响应方面有用,但下面的内容表明它还可以提高处理速度
非异步:
void CopyFile(FileInfo infile, FileInfo outFile)
{
using(var textReader = inFile.OpenText())
{
using (var textWriter = outFile.CreateText())
{
// Read a line. Wait until line read
var line = textReader.ReadLine();
while (line != null)
{
// Write the line. Wait until line written
textWrite.WriteLine(line);
// Read the next line. Wait until line read
line = textReader.ReadLine();
}
}
}
}
你看到了所有的等待。幸运的是,TextReader和TextWriter可以缓冲数据,否则我们真的必须等到数据写入后才能读取下一行
async Task CopyFileAsync(FileInfo infile, FileInfo outFile)
{
using(var textReader = inFile.OpenText())
{
using (var textWriter = outFile.CreateText())
{
// Read a line. Wait until line read
var line = await textReader.ReadLineAsync();
while (line != null)
{
// Write the line. Don't wait until line written
var writeTask = textWrite.WriteLineAsync(line);
// While the line is being written, I'm free to read the next line.
line = textReader.ReadLine();
// await until the previous line has been written:
await writeTask;
}
}
}
}
在写一行的时候,我们已经试着读下一行了。这可以提高处理速度
如果是这样的话,在使用wait-async时就没有时间增益,因为没有使用额外的线程
这是正确的。本身,async
和wait
不直接使用线程。它们的目的是释放调用线程
到目前为止,我只知道Task.Run()创建一个新线程。对于Task.WhenAll()或Task.WhenAny()也是这样吗
没有;无论是Task.whalll
还是Task.whany
都不直接使用任何线程
Whe
async Task CopyFileAsync(FileInfo infile, FileInfo outFile)
{
using(var textReader = inFile.OpenText())
{
using (var textWriter = outFile.CreateText())
{
// Read a line. Wait until line read
var line = await textReader.ReadLineAsync();
while (line != null)
{
// Write the line. Don't wait until line written
var writeTask = textWrite.WriteLineAsync(line);
// While the line is being written, I'm free to read the next line.
line = textReader.ReadLine();
// await until the previous line has been written:
await writeTask;
}
}
}
}
int foo = await FooAsync();
Task<int> task = FooAsync();
if (task is not already completed)
set continuation of task to go to "resume" on completion
return;
resume: // If we get here, task is completed
int foo = task.Result;