在不创建新线程的情况下将异步计算转换为 .NET 任务
Convert async computation to .NET task without creating a new thread
我正在通过构建 ASP.NET 核心应用程序来学习 F#。我需要实现一个接口Microsoft.AspNetCore.Identity.IUserStore<'TUser>
;此接口具有 return Task
等方法:
member this.CreateAsync (user : User, cancellationToken : CancellationToken) =
async {
let connectionString = configuration.GetConnectionString("SocialScoreApp")
let! rowCountResult = user |> createAsync connectionString
match rowCountResult with
| Ok _ -> return IdentityResult.Success
| Error e ->
let identityError = new IdentityError()
identityError.Description <- e
return IdentityResult.Failed(identityError)
} |> Async.StartAsTask
但是,根据文档,Async.StartAsTask
“在线程池上执行计算”。但这是一个 I/O 绑定操作,所以我不想涉及任何新线程。有什么办法可以实现吗?
编辑:这是createAsync
代码供参考:
let createAsync (connectionString : string) (user:User) =
let sql = "..."
let args = dict [
// ...
]
async {
use connection = new SqlConnection(connectionString)
do! connection.OpenAsync() |> Async.AwaitTask
try
let! rowCount = connection.ExecuteAsync(sql, args) |> Async.AwaitTask
return Ok rowCount
with
| e -> return Error e.Message
}
如果您查看其工作原理,StartAsTask
(source) operation creates a TaskCompletionSource
, which creates a task that is returned to the caller. This is later used to report the result of the computation. In the meantime, the computation is started using QueueAsync
, which invokes QueueWorkItemWithTrampoline
(source) 并且它使用标准 .NET ThreadPool.QueueUserWorkItem
.
这意味着当你运行StartAsTask
时,异步工作流被添加到线程池中。但是,IO 绑定操作的关键是您在代码段中使用 let!
:
let connectionString = configuration.GetConnectionString("SocialScoreApp")
let! rowCountResult = user |> createAsync connectionString
添加到线程池的工作项只会运行GetConnectionString
。然后它会调用 createAsync
,其中可能包含一些非阻塞 IO 操作。一旦评估达到这一点,线程池中的工作就会完成,它会安排在 IO 操作完成时调用回调 - 稍后调用回调并将其余计算作为新工作添加到线程池中物品。因此,您不会在 IO 操作 运行ning 时阻塞线程池。
我正在通过构建 ASP.NET 核心应用程序来学习 F#。我需要实现一个接口Microsoft.AspNetCore.Identity.IUserStore<'TUser>
;此接口具有 return Task
等方法:
member this.CreateAsync (user : User, cancellationToken : CancellationToken) =
async {
let connectionString = configuration.GetConnectionString("SocialScoreApp")
let! rowCountResult = user |> createAsync connectionString
match rowCountResult with
| Ok _ -> return IdentityResult.Success
| Error e ->
let identityError = new IdentityError()
identityError.Description <- e
return IdentityResult.Failed(identityError)
} |> Async.StartAsTask
但是,根据文档,Async.StartAsTask
“在线程池上执行计算”。但这是一个 I/O 绑定操作,所以我不想涉及任何新线程。有什么办法可以实现吗?
编辑:这是createAsync
代码供参考:
let createAsync (connectionString : string) (user:User) =
let sql = "..."
let args = dict [
// ...
]
async {
use connection = new SqlConnection(connectionString)
do! connection.OpenAsync() |> Async.AwaitTask
try
let! rowCount = connection.ExecuteAsync(sql, args) |> Async.AwaitTask
return Ok rowCount
with
| e -> return Error e.Message
}
如果您查看其工作原理,StartAsTask
(source) operation creates a TaskCompletionSource
, which creates a task that is returned to the caller. This is later used to report the result of the computation. In the meantime, the computation is started using QueueAsync
, which invokes QueueWorkItemWithTrampoline
(source) 并且它使用标准 .NET ThreadPool.QueueUserWorkItem
.
这意味着当你运行StartAsTask
时,异步工作流被添加到线程池中。但是,IO 绑定操作的关键是您在代码段中使用 let!
:
let connectionString = configuration.GetConnectionString("SocialScoreApp")
let! rowCountResult = user |> createAsync connectionString
添加到线程池的工作项只会运行GetConnectionString
。然后它会调用 createAsync
,其中可能包含一些非阻塞 IO 操作。一旦评估达到这一点,线程池中的工作就会完成,它会安排在 IO 操作完成时调用回调 - 稍后调用回调并将其余计算作为新工作添加到线程池中物品。因此,您不会在 IO 操作 运行ning 时阻塞线程池。