C# async/await for I/O-Bound vs CPU-绑定操作
C# async/await for I/O-Bound vs CPU-Bound operation
我正在学习 C# 中的异步编程。在 this 文章中,我发现对于 IO-Bound 操作你不应该使用 Task.Run() 但我不知道如何在没有 Task.Run() 的情况下创建任务......例如我想创建一个名为 GetPageCountAsync 的自定义方法,它查询数据库和 return 结果。我已经有同步查询数据库的方法 GetPageCount。我不知道比以下更好的方法:
private async Task<int> GetPageCountAsync()
{
return await Task.Run(() => GetPageCount());
}
没有 Task.Run 怎么办?我找到了 SqlCommand class 的 ExecuteReaderAsync 方法,但我想知道这个方法是如何实现的?没有 Task.Run?如果是,怎么做?
要使您的方法异步,您所要做的就是等待其中的异步操作,并像这样制作方法签名(假设此处的 return 值...):
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;
}
If you have no async methods to call in the libraries you are using you can create your own awaitables but it should be extremely rare you need to go to such lengths.
现在 GetPageCountAsync
是异步的,您只需等待它:
return await GetPageCountAsync();
对于像这样的非上下文感知代码,允许代码在另一个上下文中恢复也是一种很好的做法,例如:
return await GetPageCountAsync().ConfigureAwait(false);
如果您有其他工作要做,不依赖于查询结果,您可以启动它并等待它稍后并行执行工作:
Task pageCountTask = GetPageCountAsync();
//do something not dependent on page count....
return await pageCountTask;
有两个术语很容易混淆:多任务和多线程。
多线程是多任务处理的一种形式。很长一段时间,它实际上是我们可以使用的多任务处理的唯一方式。虽然我们可以在没有线程的情况下进行多任务处理,但编写起来通常很麻烦。在 GUI 环境中编写和使用 BackgroundWorker 比编写适当的多任务处理更容易。
但是线程有问题:需要Invoke。他们喜欢吞下例外。不要忘记,我们实际上需要一个事件队列来进行回调。
我认为 C# 5.0 添加了 Async 和 await。目标似乎是允许多任务处理 而无需 多线程或过多的编写代码的需要。编译器和运行时将为您在同一线程内的上下文之间进行所有烦人的切换。您仍然可以为此使用线程。您必须将它们用于 CPU 绑定操作。但在很多情况下,使用多任务处理更简单更好。
您用来执行 IO 操作的任何框架都有责任为您提供固有的异步操作。它可以提供返回 Task
的方法,或提供一些其他异步操作(即接受回调、returns 和 IAsyncResult
、触发事件等的方法)变成了Task
.
如果您调用的操作已经同步阻塞线程,直到异步结果完成,那么已经太晚了。 大多数 数据库框架都擅长提供某种形式的异步查询数据库的方式,因此很可能还有另一种方法供您使用。如果您使用的框架 不 提供该方法的任何异步版本,那么您无法避免使用多线程;该框架的作者已将您的选择权从您手中夺走。
我正在学习 C# 中的异步编程。在 this 文章中,我发现对于 IO-Bound 操作你不应该使用 Task.Run() 但我不知道如何在没有 Task.Run() 的情况下创建任务......例如我想创建一个名为 GetPageCountAsync 的自定义方法,它查询数据库和 return 结果。我已经有同步查询数据库的方法 GetPageCount。我不知道比以下更好的方法:
private async Task<int> GetPageCountAsync()
{
return await Task.Run(() => GetPageCount());
}
没有 Task.Run 怎么办?我找到了 SqlCommand class 的 ExecuteReaderAsync 方法,但我想知道这个方法是如何实现的?没有 Task.Run?如果是,怎么做?
要使您的方法异步,您所要做的就是等待其中的异步操作,并像这样制作方法签名(假设此处的 return 值...):
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;
}
If you have no async methods to call in the libraries you are using you can create your own awaitables but it should be extremely rare you need to go to such lengths.
现在 GetPageCountAsync
是异步的,您只需等待它:
return await GetPageCountAsync();
对于像这样的非上下文感知代码,允许代码在另一个上下文中恢复也是一种很好的做法,例如:
return await GetPageCountAsync().ConfigureAwait(false);
如果您有其他工作要做,不依赖于查询结果,您可以启动它并等待它稍后并行执行工作:
Task pageCountTask = GetPageCountAsync();
//do something not dependent on page count....
return await pageCountTask;
有两个术语很容易混淆:多任务和多线程。
多线程是多任务处理的一种形式。很长一段时间,它实际上是我们可以使用的多任务处理的唯一方式。虽然我们可以在没有线程的情况下进行多任务处理,但编写起来通常很麻烦。在 GUI 环境中编写和使用 BackgroundWorker 比编写适当的多任务处理更容易。
但是线程有问题:需要Invoke。他们喜欢吞下例外。不要忘记,我们实际上需要一个事件队列来进行回调。
我认为 C# 5.0 添加了 Async 和 await。目标似乎是允许多任务处理 而无需 多线程或过多的编写代码的需要。编译器和运行时将为您在同一线程内的上下文之间进行所有烦人的切换。您仍然可以为此使用线程。您必须将它们用于 CPU 绑定操作。但在很多情况下,使用多任务处理更简单更好。
您用来执行 IO 操作的任何框架都有责任为您提供固有的异步操作。它可以提供返回 Task
的方法,或提供一些其他异步操作(即接受回调、returns 和 IAsyncResult
、触发事件等的方法)变成了Task
.
如果您调用的操作已经同步阻塞线程,直到异步结果完成,那么已经太晚了。 大多数 数据库框架都擅长提供某种形式的异步查询数据库的方式,因此很可能还有另一种方法供您使用。如果您使用的框架 不 提供该方法的任何异步版本,那么您无法避免使用多线程;该框架的作者已将您的选择权从您手中夺走。