为了最快的学习..
理解方法执行流程(附图表):3分钟
在这张图片中,只关注#6(仅此而已)
在第 6 步,执行用完了工作并停止了。要继续,它需要来自 getStringTask(一种函数)的结果。因此,它使用一个await
操作符来暂停它的进程,并将控制权交还给调用者(我们所在的这个方法)。对 getStringTask 的实际调用是在 #2 早些时候进行的。在 #2 处,承诺返回一个字符串结果。但是它什么时候会返回结果呢?我们(#1:AccessTheWebAsync)是否应该再次拨打第二个电话?谁得到结果,#2(调用语句)或#6(等待语句)?
AccessTheWebAsync() 的外部调用者现在也在等待。所以调用者在等待 AccessTheWebAsync,而 AccessTheWebAsync 目前正在等待 GetStringAsync。有趣的是,AccessTheWebAsync 在等待之前做了一些工作(#4),也许是为了节省等待时间。外部调用者(以及链中的所有调用者)也可以使用同样的多任务自由,这是这种“异步”事物的最大优势!您觉得它是同步的..或正常的,但事实并非如此。
#2 和 #6 是分开的,所以我们有 #4 的优势(等待时工作)。但我们也可以做到不拆分。所以#2 将是:string urlContents = await client.GetStringAsync("...");
. 在这里我们看不到任何优势,但是在链中的某个地方,一个函数将被拆分,而其余的函数则在不拆分的情况下调用它。这取决于您使用的链中的哪个函数/类。这种从函数到函数的行为变化是这个主题中最令人困惑的部分。
请记住,该方法已经返回(#2),它不能再次返回(没有第二次)。那么来电者怎么知道呢?一切都与任务有关!任务被退回。等待任务状态(不是方法,不是值)。值将在任务中设置。任务状态将设置为完成。调用者只监视任务(#6)。所以 6# 是哪里/谁得到结果的答案。稍后在此处进一步阅读。
为学习而自省:1 分钟
让我们稍微调整一下问题:
如何以及何时使用and? async
await
Tasks
因为学习Task
会自动涵盖其他两个(并回答您的问题)。
整个想法很简单。一个方法可以返回任何数据类型(double、int、object 等),但在这里我们只是否认这一点并强制Task
返回一个 ' ' 对象!但是我们仍然需要返回数据(除了 void),对吧?这将在“ Task
”对象内的标准属性中设置,例如:“ Result
”属性。
快速了解语法糖:5 分钟
internal static int Method(int arg0, int arg1)
{
int result = arg0 + arg1;
IO(); // Do some long running IO.
return result;
}
internal static Task<int> MethodTask(int arg0, int arg1)
{
Task<int> task = new Task<int>(() => Method(arg0, arg1));
task.Start(); // Hot task (started task) should always be returned.
return task;
}
我们提到了等待或异步吗?不。调用上述方法,您将获得一个可以监控的任务。您已经知道任务返回什么......一个整数。
- 调用任务有点棘手,那就是关键字开始出现的时候。如果有一个方法调用原始方法(非异步),那么我们需要编辑它,如下所示。让我们调用 MethodTask()
internal static async Task<int> MethodAsync(int arg0, int arg1)
{
int result = await HelperMethods.MethodTask(arg0, arg1);
return result;
}
上面添加的相同代码如下图所示:
- 我们正在“等待”任务完成。因此
await
(强制语法)
- 由于我们使用await,所以我们必须使用
async
(强制语法)
- MethodAsync
Async
作为前缀(编码标准)
await
很容易理解,但剩下的两个 ( async
, Async
) 可能不是:)。好吧,它应该对编译器更有意义。稍后在此处进一步阅读
所以有2个部分。
创建“任务”(只有一个任务,这将是一种附加方法)
创建用于调用任务的语法糖await+async
(如果要转换非异步方法,这涉及更改现有代码)
请记住,我们有一个 AccessTheWebAsync() 的外部调用者,并且该调用者也不能幸免……即它也需要相同的调用者await+async
。并且链条还在继续(因此这是一个可能影响许多类的重大变化)。它也可以被认为是非破坏性更改,因为原始方法仍然可以调用。如果您想进行重大更改,请更改它的访问权限(或删除并将其移动到任务中),然后类将被迫使用任务方法。无论如何,在异步调用中总会有一个Task
在一端并且只有一个。
一切都好,但一位开发人员惊讶地发现Task
失踪......
分享开发者的困惑:5 分钟
开发人员犯了不实施的错误,Task
但它仍然有效!尝试理解问题以及此处提供的已接受答案。希望您已阅读并完全理解。总结是我们可能看不到/实现“任务”,但它在父/关联类的某个地方实现。同样,在我们的示例中,调用已经构建的方法比我们自己使用( )MethodAsync()
实现该方法要容易得多。大多数开发人员在将代码转换为异步代码时很难理解。Task
MethodTask()
Tasks
提示:尝试找到现有的 Async 实现(如MethodAsync
或ToListAsync
)来外包困难。所以我们只需要处理 Async 和 await (这很简单,与普通代码非常相似)
问题:快速将普通代码的实际实现更改为异步操作:2 分钟
下面数据层中显示的代码行开始中断(很多地方)。因为我们将部分代码从 .Net framework 4.2.* 更新到了 .Net core。我们必须在整个应用程序中在 1 小时内解决这个问题!
var myContract = query.Where(c => c.ContractID == _contractID).First();
十分简单!
- 我们安装了 EntityFramework nuget 包,因为它具有 QueryableExtensions。或者换句话说,它执行异步实现(任务),所以我们可以用简单
Async
的await
代码生存。
- 命名空间 = Microsoft.EntityFrameworkCore
调用代码行变成了这样
var myContract = await query.Where(c => c.ContractID == _contractID).FirstAsync();
- 方法签名从
Contract GetContract(int contractnumber)
到
async Task<Contract> GetContractAsync(int contractnumber)
- 调用方法也受到影响:
GetContract(123456);
被称为GetContractAsync(123456).Result;
等待!那是什么Result
?接得好!GetContractAsync
只返回一个Task
不是我们想要的值(Contract
)。一旦操作的结果可用,它将被存储并在后续调用该Result
属性时立即返回。我们也可以使用类似的“Wait()”来实现超时
TimeSpan ts = TimeSpan.FromMilliseconds(150);
if (!t.Wait(ts)) Console.WriteLine("超时时间已过");
- 我们在 30 分钟内改变了它!
但是架构师告诉我们不要仅仅为此使用 EntityFramework 库!哎呀!戏剧!然后我们做了一个自定义任务实现(yuk!)。你知道怎么做。还是很轻松!..还是不错的..
下一步去哪里?
有一个精彩的快速视频我们可以观看关于在 ASP.Net Core 中将同步调用转换为异步,也许这可能是阅读本文后的方向。还是我解释得够多了?;)