那么,
SaveChanges()
和SaveChangesAsync()
之间有什么区别呢?以及
Find()
和FindAsync()
之间呢?在服务器端,当我们使用
Async
方法时,还需要添加await
。因此,我不认为这在服务器端是异步的。这只有帮助防止客户端浏览器上的UI阻塞吗?或者它们之间有什么利弊呢?
SaveChanges()
和SaveChangesAsync()
之间有什么区别呢?Find()
和FindAsync()
之间呢?Async
方法时,还需要添加await
。因此,我不认为这在服务器端是异步的。SaveChanges()
和 SaveChangesAsync()
作为示例,但同样适用于 Find()
和 FindAsync()
。myList
,您需要将它们添加到数据库中。要插入这些项目,您的函数可能如下所示: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()
完成时的位置。感到困惑了吗?下面是一个输出示例:
Function Starting Save Starting Continuing to execute! Continuing to execute! Continuing to execute! Continuing to execute! Continuing to execute! .... Continuing to execute! Save Complete! Continuing to execute! Continuing to execute! Continuing to execute! .... Continuing to execute! Function Complete!
或者是这样的:
Function Starting Save Starting Continuing to execute! Continuing to execute! Save Complete! Continuing to execute! Continuing to execute! Continuing to execute! .... Continuing to execute! Function Complete!
这就是 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());
}
在这里,你有四个不同的保存记录函数同时执行。使用async await
来完成MyCallingFunction
会比按顺序调用各个SaveRecords
函数要快得多。
我还没有涉及到的一件事是await
关键字。它的作用是停止当前函数的执行,直到等待的Task
完成。因此,在原始MyCallingFunction
中,只有当SaveRecords
函数完成后,才会将Function Complete
写入控制台。
长话短说,如果你有使用async await
的选项,应该使用它,因为它会大大提高应用程序的性能。
await
,即使在调用SaveChanges之后您不需要做任何其他事情,ASP也会说:“啊哈,这个线程返回等待异步操作,这意味着我可以让这个线程同时处理一些其他请求!” 这使得您的应用程序水平扩展更好。 - saraawait
等待SaveChangesAsync
。
https://learn.microsoft.com/en-us/ef/core/saving/async 此外,使用这些异步方法实际上有很大的优势。例如,在保存数据或执行大量操作时,您可以在WebApi中继续接收其他请求,或者在桌面应用程序中不冻结界面以改善用户体验。 - tgarciausing System;
using System.Threading;
using System.Threading.Tasks;
using static System.Console;
public static class Program
{
const int N = 20;
static readonly object obj = new object();
static int counter;
public static void Job(ConsoleColor color, int multiplier = 1)
{
for (long i = 0; i < N * multiplier; i++)
{
lock (obj)
{
counter++;
ForegroundColor = color;
Write($"{Thread.CurrentThread.ManagedThreadId}");
if (counter % N == 0) WriteLine();
ResetColor();
}
Thread.Sleep(N);
}
}
static async Task JobAsync()
{
// intentionally removed
}
public static async Task Main()
{
// intentionally removed
}
}
static async Task JobAsync()
{
Task t = Task.Run(() => Job(ConsoleColor.Red, 1));
Job(ConsoleColor.Green, 2);
await t;
Job(ConsoleColor.Blue, 1);
}
public static async Task Main()
{
Task t = JobAsync();
Job(ConsoleColor.White, 1);
await t;
}
备注:由于JobAsync
的同步部分(绿色)花费的时间比任务t
(红色)更长,因此在await t
的时候任务t
已经完成。结果是,延续部分(蓝色)会在与绿色一样的线程上运行。
Main
方法中的同步部分(白色)将在绿色部分完成后开始运行。这就是为什么异步方法中的同步部分有问题的原因。
static async Task JobAsync()
{
Task t = Task.Run(() => Job(ConsoleColor.Red, 2));
Job(ConsoleColor.Green, 1);
await t;
Job(ConsoleColor.Blue, 1);
}
public static async Task Main()
{
Task t = JobAsync();
Job(ConsoleColor.White, 1);
await t;
}
备注: 本案例与第一种情况相反。 JobAsync
的同步部分(绿色)旋转的时间比任务t
(红色)短,所以在执行到await t
时,任务t
尚未完成,导致后续过程(蓝色)在另一个线程上运行。
Main
的同步部分(白色)在绿色同步部分完成旋转后仍然持续旋转。
static async Task JobAsync()
{
Task t = Task.Run(() => Job(ConsoleColor.Red, 1));
await t;
Job(ConsoleColor.Green, 1);
Job(ConsoleColor.Blue, 1);
}
public static async Task Main()
{
Task t = JobAsync();
Job(ConsoleColor.White, 1);
await t;
}
备注:这个案例将解决之前异步方法中同步部分的问题。任务t
会立即等待,因此继续执行(蓝色部分)在不同的线程上运行,与绿色部分并行。 Main
函数的同步部分(白色部分)将与JobAsync
立即并行。
如果您想添加其他案例,请随意编辑。
这个说法是不正确的:
在服务器端使用异步方法时,我们也需要添加 await。
你不需要添加 "await",await
只是 C# 中一个方便的关键字,它使你能够在调用后编写更多的代码行,而那些其他行只有在保存操作完成后才会执行。但正如你指出的那样,你可以通过调用 SaveChanges
而不是 SaveChangesAsync
来实现这一点。
但从根本上讲,异步调用涉及到更多的内容。这里的想法是,如果在保存操作进行时,服务器上还有其他工作可以做,那么你应该使用 SaveChangesAsync
。不要使用 "await"。只需调用 SaveChangesAsync
,然后继续并行执行其他任务。这包括在 Web 应用程序中,在保存完成之前向客户端返回响应。但当然,你仍然需要检查保存的最终结果,以便在失败时将其通知给用户或以某种方式记录下来。
SaveChangesAsync
。调用SaveChanges
会启动新线程并浪费服务器资源。 - Shadow