假设我想从F#调用这个C#函数:
public static class Foo {
public Bar Baz()
{
...
}
}
问题是这个功能是CPU密集型的,我不想阻止它。不幸的是,C#库没有Task<Bar> BazAsync()
重载。
然后我想通过创建一个调用它的F#函数并返回一个(已经启动的)Async<Bar>
来自己提供异步版本。也就是说,我不想使用System.Threading.Task<Bar>
。
我想我正在寻找的东西相当于F#异步处理方式中的Task<T>.Run()
。
我已经看过以下选项:
Async.StartAsTask
- &gt;处理C#ish任务类型。Async.Start
和Async.StartImmediately
- &gt;收到Async<unit>
而不是Async<T>
Async.StartChild
我正在寻找什么?
如果是,那将是:
let BazWrapper() =
let asyncTask: Async<Bar> = async {
return Foo.Bar()
}
Async.StartChild asyncTask
但是,如果以上是解决方案:
Async<Async<Bar>>
而不是Async<Bar>
?答案 0 :(得分:6)
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
}
但是,我怀疑你会发现已经启动的异步对你来说不如“冷”异步(一个尚未启动的异步)有用,因为“冷”异步仍然可以在启动之前由其他异步任务组成。 (这可以用于,例如,包装你的asyncs周围的日志。)所以,如果我正在为你的情况编写代码,我可能只是这样做:
let BazWrapper() =
async {
return Foo.Bar()
}
现在BazWrapper()返回一个尚未启动的Async,如果你想立即使用值,你可以用Async.RunSynchronously
启动它,或者在其他async
计算表达式中使用它