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

时间:2018-02-05 14:18:46

标签: c# asynchronous io async-await cpu

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

更好的方式
private async Task<int> GetPageCountAsync()
{
  return await Task.Run(() => GetPageCount());
}

如何在没有Task.Run的情况下执行此操作?我找到了SqlCommand类的ExecuteReaderAsync方法,但我想知道这个方法是如何实现的?没有Task.Run?如果是这样,怎么样?

3 个答案:

答案 0 :(得分:5)

您正在使用的任何框架都有责任执行IO操作,以便为您提供固有的异步操作。它可以提供一个返回Task的方法,或者一些其他异步操作(即一个接受回调的方法,返回一个IAsyncResult,触发一个事件等),可以转换为{ {1}}。

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

答案 1 :(得分:0)

要使你的方法异步,你所要做的就是等待其中的异步操作,并像这样设置方法签名(假设返回值在这里......):

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;
}
  

如果你没有异步方法来调用你正在使用的库,你可以create your own awaitables,但是你需要达到这样的长度是非常罕见的。

既然GetPageCountAsync是异步的,你只需等待它:

return await GetPageCountAsync();

这样的非上下文感知代码也是一种很好的做法,允许代码在另一个上下文中恢复,如:

return await GetPageCountAsync().ConfigureAwait(false);

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

 Task pageCountTask = GetPageCountAsync();
//do something not dependent on page count....
return await pageCountTask;

答案 2 :(得分:0)

有两个术语可以轻松混合:多任务处理和多线程处理。

多线程是多任务处理的一种形式。很长一段时间,它实际上是我们可以使用的多任务的唯一方式。虽然我们可以在没有线程的情况下进行多任务处理,但这通常很麻烦。在GUI环境中编写和使用BackgroundWorker更容易,然后编写适当的多任务处理。

但是线程有问题:需要调用。他们喜欢吞下例外。不要忘记,我们实际上需要一个事件队列来进行回调。

Async&amp;我觉得await已经添加了C#5.0。目标似乎是允许多任务没有多线程或过度需要编写代码。编译器和运行时将为您在同一个线程中的上下文之间进行所有令人讨厌的切换。你仍然可以使用Threads。您必须将它们用于CPU绑定操作。但是对于许多使用多任务处理的情况更简单,更好。