在 Entity Framework 中,我应该何时使用 FirstOrDefaultAsync 还是 FirstOrDefault?

3
我正在使用Entity Framework在我的ASP.NET Web API中。在查询方法中,我可以选择使用标准的LINQ方法FirstOrDefault()或EF方法FirstOrDefaultAsync()。什么时候应该使用其中之一?
据我所知,使用:

var users = context.Users.FirstOrDefault(user => user.id == Id);

并且

var users = await context.Users.FirstOrDefaultAsync(user => user.id == Id);

这两种方法都会暂停它们所在的线程,直到调用返回。 Async 方法的唯一优点是它可以在 Async 方法中进行 Awaited。但是,如果该方法也被等待,则无关紧要?除非你肯定会触发另一个线程的创建。

public async Task MethodOneAsync() {
  await MethodTwoAsync();
}

public async Task MethodTwoAsync() {
  await context.Users.FirstOrDefaultAsync(..);
  ...
}

Is this the same as doing:

public async void MethodOneAsync() {
  MethodTwo();
  ... await something else to 'please' the async nature, or remove async all together ...
}

public void MethodTwo() {
  context.Users.FirstOrDefault(..);
  ...
}

谢谢,我只是想澄清自己的困惑。

注意:这是我所指的示例。

Async method chain

Sync method chain

timings and orders

这些展示了我所说的链式结构,时间(9和0)以毫秒为单位计算。因此,相对来说,异步方法需要更长时间才能运行。
编辑: 在评论中已经说明了这样做的原因是为了释放线程,但是如果被调用的方法是服务器内部的,则它不是释放一个线程只是为了启动另一个线程来运行被调用的代码吗?

2
不会。第一种方法在等待数据库返回时会阻塞线程,占用了本可以用来处理传入的网络请求(或其他工作)的线程;而第二种方法则安排了一个继续执行的任务,在数据库操作完成后恢复执行,释放当前线程以处理其他请求。 - Kenneth K.
我完全理解您的观点,但是如果您正在创建一个异步链->等待->异步链->等待链,那么前一个方法无论如何都不能继续执行,直到后续的异步方法返回。 - Tristan Trainer
是的,但这并不意味着线程被阻塞。 - Kenneth K.
啊,我明白了,所以它会释放线程供其他地方使用?直到调用继续执行的时候? - Tristan Trainer
2个回答

9

考虑其他人的需要!

我开个玩笑,但这确实是重点。同步方法将强制线程等待返回结果。

异步方法将释放线程来处理其他事务。例如,在ASP.NET中,线程可以开始处理刚到达的其他用户请求。这点特别重要,因为ASP.NET的线程数量有限。

在桌面应用程序中,如果您正在使用UI线程,则它将释放线程以响应用户输入。(我相信你已经遇到过应用程序凝固的情况 - 那很烦人)

有一篇非常好的微软文章可以帮助澄清这些内容:使用async和await进行异步编程


2
太棒了,非常有帮助,谢谢!我之前没有意识到 Await 会释放线程供其他用途,然后在任务返回时继续执行!我原以为它会因为 await 而阻塞调用线程!:) - Tristan Trainer
我刚刚意识到一个问题.. 是的,它释放了调用线程,但是另一个线程被用于创建的任务,所以仍然使用相同数量的线程。唯一合理的情况是当你调用外部服务时,对吗?那个服务将有自己的一组线程? - Tristan Trainer
1
任务不一定会创建另一个线程。它只应该用于I/O类型的操作(从网络读取或硬盘读取),在等待期间 CPU 真的没有其他事情可做的情况下使用。如果尝试将其用于 CPU 处理,除了将其移到另一个线程中(在桌面应用程序中可能是有意义的)外,并没有任何好处。 - Gabriel Luci
2
这是一篇有帮助的阅读材料:没有线程 - Gabriel Luci
太完美了,谢谢你。除了将其移到另一个线程之外,没有其他好处。这就是我的思考过程,如果是内部的话,那就不会改变任何事情。然而,对于外部的情况,我完全理解。我会阅读一下,谢谢 :) - Tristan Trainer
只要记住它是CPU外部的。如果你正在读取硬盘,仍然会有等待时间,而其他一些操作甚至可以在这段短时间内完成。在你使用ASP.NET和Entity Framework的例子中,尽可能使用异步操作更加合理。 - Gabriel Luci

4

异步/等待并不总是更好。 .Net CLR必须管理线程,这会产生开销。因此,如果是一个小型数据表,那么执行.FirstOrDefault() 可能会更快。但是,如果有预期调用可能需要一段时间才能生成结果,则应使用.FirstOrDefaultAsync()。


3
请记住,在计算机世界里,“一会儿”的时间与人类世界中的“一会儿”是不同的。CPU在100毫秒内可以完成许多工作。 - Gabriel Luci
当AI机器人接管时,愿上帝保佑我们。 - Fandango68

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接