只要您需要在远程服务器上执行操作,您的程序就会生成请求、发送请求,然后等待响应。我将以SaveChanges() 和SaveChangesAsync() 为例,但同样适用于Find() 和FindAsync()。
假设您有一个列表myList,其中包含需要添加到数据库中的 100 多个项目。要插入它,您的函数将如下所示:
using(var context = new MyEDM())
{
context.MyTable.AddRange(myList);
context.SaveChanges();
}
首先创建MyEDM 的实例,将列表myList 添加到表MyTable,然后调用SaveChanges() 将更改持久化到数据库。它按照您的意愿工作,记录被提交,但您的程序在提交完成之前不能做任何其他事情。这可能需要很长时间,具体取决于您的承诺。如果您要提交对记录的更改,实体必须一次提交这些更改(我曾经保存需要 2 分钟进行更新)!
要解决此问题,您可以执行以下两项操作之一。首先是您可以启动一个新线程来处理插入。虽然这将释放调用线程以继续执行,但您创建了一个新线程,它将坐在那里等待。不需要这种开销,这就是 async await 模式解决的问题。
对于 I/O 操作,await 很快成为您最好的朋友。取上面的代码部分,我们可以修改为:
using(var context = new MyEDM())
{
Console.WriteLine("Save Starting");
context.MyTable.AddRange(myList);
await context.SaveChangesAsync();
Console.WriteLine("Save Complete");
}
这是一个非常小的变化,但会对代码的效率和性能产生深远的影响。那么会发生什么?代码的开头是相同的,您创建一个MyEDM 的实例并将您的myList 添加到MyTable。但是当您调用await context.SaveChangesAsync() 时,代码的执行返回到调用函数! 因此,当您等待所有这些记录提交时,您的代码可以继续执行。假设包含上述代码的函数的签名为public async Task SaveRecords(List<MyTable> saveList),调用函数可能如下所示:
public async Task MyCallingFunction()
{
Console.WriteLine("Function Starting");
Task saveTask = SaveRecords(GenerateNewRecords());
for(int i = 0; i < 1000; i++){
Console.WriteLine("Continuing to execute!");
}
await saveTask;
Console.Log("Function Complete");
}
我不知道为什么会有这样的函数,但它的输出显示了async await 的工作原理。首先让我们回顾一下发生了什么。
执行进入MyCallingFunction,Function Starting 然后Save Starting 被写入控制台,然后函数SaveChangesAsync() 被调用。此时,执行返回MyCallingFunction,并进入for循环写入'Continuing to Execute'最多1000次。当SaveChangesAsync() 完成时,执行返回到SaveRecords 函数,将Save Complete 写入控制台。一旦SaveRecords 中的所有内容完成,将在MyCallingFunction 中继续执行,就像SaveChangesAsync() 完成时一样。使困惑?这是一个示例输出:
功能启动
保存开始
继续执行!
继续执行!
继续执行!
继续执行!
继续执行!
……
继续执行!
保存完成!
继续执行!
继续执行!
继续执行!
……
继续执行!
功能齐全!
或许:
功能启动
保存开始
继续执行!
继续执行!
保存完成!
继续执行!
继续执行!
继续执行!
……
继续执行!
功能齐全!
这就是async await 的美妙之处,您的代码可以在等待完成的同时继续运行。实际上,您将有一个更像这样的函数作为您的调用函数:
public async Task MyCallingFunction()
{
List<Task> myTasks = new List<Task>();
myTasks.Add(SaveRecords(GenerateNewRecords()));
myTasks.Add(SaveRecords2(GenerateNewRecords2()));
myTasks.Add(SaveRecords3(GenerateNewRecords3()));
myTasks.Add(SaveRecords4(GenerateNewRecords4()));
await Task.WhenAll(myTasks.ToArray());
}
在这里,您有四个不同的保存记录功能同时进行。 MyCallingFunction 使用 async await 将比连续调用单个 SaveRecords 函数快得多。
我还没有提到的一件事是await 关键字。它的作用是停止当前函数的执行,直到您等待的 Task 完成为止。所以对于原始的MyCallingFunction,在SaveRecords 函数完成之前,Function Complete 行不会被写入控制台。
长话短说,如果您可以选择使用async await,您应该这样做,因为它会大大提高您的应用程序的性能。