c#webapi:等待任务.Run vs更多granualar等待

本文关键字:等待 更多 granualar vs 任务 c#webapi Run | 更新日期: 2023-09-27 18:21:20

根据本文,我在WebApi控制器中使用async/await:https://msdn.microsoft.com/en-us/magazine/dn802603.aspx

Hava看看我的控制器中的简化代码:

DataBaseData = await Task.Run( () => GetDataFunction()  );

GetDataFunction是一个打开数据库连接、打开读取器并从数据库中读取数据的函数。

在许多例子中,我看到它的处理方式有所不同。正在等待GetDataFunction本身。在该职能范围内,每一步都在等待。例如:

  • connection.OpenAsync
  • reader.ReadAsycnc
  • reader.IsDBNullAsync

为什么这种做法很好?为什么不启动一个线程来访问整个数据库(使用Task.Run)?

更新:谢谢你的帮助。现在我明白了。我没有明白,异步Api本身不会启动线程。这真的很有帮助:blog.stephencleary.com/2013/11-there-is-no-thread.html

c#webapi:等待任务.Run vs更多granualar等待

您链接到的文章声明:

您可以通过等待Task.Run来启动一些后台工作,但这样做毫无意义。事实上,这实际上会干扰ASP.NET线程池启发法,从而损害您的可扩展性。。。一般来说,不要将工作排队到ASP.NET上的线程池。

换句话说,在ASP.NET上避免Task.Run

为什么这种做法很好?为什么不启动一个线程来访问整个数据库(使用Task.Run)?

这是因为异步API不使用其他线程async/await的全部目的是释放当前线程而不是使用另一个线程。我有一篇博客文章描述了async如何在不需要线程的情况下工作。

因此,Task.Run(或自定义线程)方法在从数据库获取数据时会占用(阻塞)一个线程。适当的异步方法(例如,EF6或ADO.NET异步API)不会;它们允许请求线程用于其他请求,而该请求正在等待数据库响应。

我想你想知道为什么要使用

await Task.Run(() => GetDataFunction());

而不是

await GetDataFunction();

正如您在Task.Run(Func)中备注一节下所看到的,其中写道:

语言编译器使用Run(Func)方法来支持异步和等待关键字。它不打算直接从用户代码中调用。

也就是说,您应该使用await GetDataFunction

这一切都是关于资源管理和共享的。

当使用connection.Open()方法时,调用它的线程必须等待连接真正打开。在等待期间,它只消耗操作系统分配给它的CPU片。但是,当await connection.OpenAsync()时,线程会释放其资源,操作系统可以将这些资源重新分配给其他线程。

现在,有一个问题:要想真正从异步调用中获益,它们应该比操作系统切换上下文所需的时间更长,否则会导致应用程序性能下降。

connection.OpenAsync()reader.ReadAsync()等上等待的做法是因为这些操作可能是长时间运行的操作。在一个去数据库驻留在另一台机器上并且请求量很大的系统中,连接到数据库并获得查询结果将需要一些时间。与其在等待结果到达时阻塞CPU,为什么不将该时间片分配给在调度程序队列中等待为另一个客户端呈现响应的另一个工作线程呢?

现在,关于启动另一个线程进行数据访问:不要这样做。每个新线程都被分配了大约1MB的内存空间,所以除了在等待数据库操作完成时浪费CPU时间之外,您还将浪费内存。此外,拥有如此大的内存占用将需要更多的垃圾回收器运行,将在运行时冻结所有线程。

使用Task.Run()将从ThreadPool调度线程上的数据访问操作,但在等待数据库服务器响应时,您将失去与另一个线程共享CPU时间的好处。