假设我想从 F# 调用这个 C# 函数:
public static class Foo {
public Bar Baz()
{
...
}
}
问题是这个功能是 CPU 密集型的,我不想阻止它。不幸的是,C# 库没有
Task<Bar> BazAsync()
重载。然后我想自己提供异步版本,方法是创建一个 F# 函数来调用它并返回(已经开始)
Async<Bar>
.也就是说,我不想使用 System.Threading.Task<Bar>
.我想我正在寻找的是相当于
Task<T>.Run()
在 F# Async 处事方式中。我已经看过以下选项:
Async.StartAsTask
-> 处理 C#ish 任务类型。 Async.Start
和 Async.StartImmediately
-> 接收 Async<unit>
不是 Async<T>
是
Async.StartChild
我在找什么?如果是,那将是:
let BazWrapper() =
let asyncTask: Async<Bar> = async {
return Foo.Bar()
}
Async.StartChild asyncTask
但是,如果以上是解决方案:
Async<Async<Bar>>
而不是 Async<Bar>
? 最佳答案
BazWrapper 返回 Async<Async<Bar>>
因为这就是 StartChild 所做的:它需要一个 Async<'T>
并返回 Async<Async<'T>>
.目的是在异步计算表达式中使用它,以便您可以启动多个“子”异步。来自 Async.Start vs Async.StartChild 的示例示例代码:
async {
//(...async stuff...)
for msg in msgs do
let! child = asyncSendMsg msg |> Async.StartChild
()
//(...more async stuff...)
}
当您在
async
内时计算表达式,let!
关键字将“解包”Async<'Whatever>
为您留下 'Whatever
类型的值.在调用Async.StartChild
的情况下, 'Whatever
类型具体为 Async<'T>
.因此,如果你想通过
Async.StartChild
返回一个已经启动的异步。 ,这样做的方法是:let BazWrapper() =
let asyncTask: Async<Bar> = async {
return Foo.Bar()
}
async {
let! child = Async.StartChild asyncTask
return! child
}
但是,我怀疑您会发现已经启动的异步对您而言不如“冷”异步(尚未启动的异步)有用,因为“冷”异步仍然可以与其他启动之前的异步任务。 (例如,这对于将日志记录在您的异步中很有用。)因此,如果我正在为您的情况编写代码,我可能会简单地这样做:
let BazWrapper() =
async {
return Foo.Bar()
}
现在 BazWrapper() 返回一个尚未启动的异步,您可以使用
Async.RunSynchronously
启动它如果您想要立即获得该值,或者在其他 async
中使用它计算表达式。
关于asynchronous - 从 C# 调用非异步 Func<T> 返回 F# 函数中的启动 Async<T>?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49463852/