C#async/await for I/O-Bound vs CPU-Bound操作

Kle*_*nko 4 c# io cpu asynchronous async-await

我正在学习C#中的异步编程.在文章中,我发现,对于IO绑定操作,你不应该使用Task.Run(),但我不知道如何创建无Task.Run()任务......比如我想创建一个名为自定义方法GetPageCountAsync,查询数据库并返回结果.我有方法GetPageCount已经查询数据库同步.我不知道比以下更好的方式:

private async Task<int> GetPageCountAsync()
{
  return await Task.Run(() => GetPageCount());
}
Run Code Online (Sandbox Code Playgroud)

没有Task.Run怎么做?我找到了SqlCommand类的ExecuteReaderAsync方法,但我想知道这个方法是如何实现的?没有Task.Run?如果是这样,怎么样?

Ser*_*rvy 6

您正在使用的任何框架负责执行IO操作,以便为您提供固有的异步操作.它可以提供一个方法返回一个Task或一些其他异步操作(即一个接受回调的方法,返回一个IAsyncResult,触发一个事件等),这个操作可以变成一个Task.

如果你正在调用的操作已经同步阻塞线程,直到异步结果完成,那么它已经太晚了. 大多数 DB框架都很好地提供了某种形式的查询数据库的异步方式,所以可能的另一种方法是让你使用.如果您使用的框架提供该方法的任何异步版本,那么您无法避免使用多个线程; 框架的作者已经从你手中接受了这个选择.


Cro*_*der 1

要使您的方法异步,您所要做的就是等待其中的异步操作,并使方法签名如下(假设此处的返回值...):

async Task<int> GetPageCountAsync()
{
    //not shown is how to set up your connection and command
    //nor disposing resources
    //nor validating the scalar value
    var pages = await command.ExecuteScalarAsync().ConfigureAwait(false);
    return (int)pages;
}
Run Code Online (Sandbox Code Playgroud)

如果您没有异步方法可以在您正在使用的库中调用,您可以创建自己的等待对象,但您需要这样做的情况应该很少见。

现在这GetPageCountAsync是异步的,你只需等待它:

return await GetPageCountAsync();
Run Code Online (Sandbox Code Playgroud)

对于像这样的非上下文感知代码来说,允许代码在另一个上下文上恢复也是一个很好的做法,例如:

return await GetPageCountAsync().ConfigureAwait(false);
Run Code Online (Sandbox Code Playgroud)

如果您还有其他工作要做,不依赖于查询结果,您可以启动它并稍后等待它以并行执行工作:

 Task pageCountTask = GetPageCountAsync();
//do something not dependent on page count....
return await pageCountTask;
Run Code Online (Sandbox Code Playgroud)

  • 我认为OP想知道如何创建异步IO而不是如何使用它 - &gt;这并不能真正回答我的问题 (3认同)