假设我想从f#,此c#函数:
打电话给public static class Foo {
public Bar Baz()
{
...
}
}
问题是此功能是CPU密集型的,我不想阻止它。不幸的是,C#库没有Task<Bar> BazAsync()
超载。
然后,我想通过创建一个调用它并返回(已经启动(Async<Bar>
的F#函数来自己提供异步版本。也就是说,我不想使用System.Threading.Task<Bar>
。
我猜我要寻找的是f#async做事方式中的 Task<T>.Run()
。
我已经查看了以下选项:
-
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
但是,如果以上是解决方案:
- 为什么大多数围绕f#async工作流的文档不提起启动孩子?
- 为什么BazWrapper返回
Async<Async<Bar>>
而不是Async<Bar>
?
bazwrapper返回 Async<Async<Bar>>
,因为那是起始人的工作:它需要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
返回已经启动的 async,那么这样做的方法是:
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
计算表达式中使用。