通过从C#调用非异步Func <T>,返回F#函数中的异步<T>?

kno*_*cte 4 f# asynchronous computation-expression c#-to-f#

假设我想从F#调用这个C#函数:

public static class Foo {
    public Bar Baz()
    {
       ...
    }
}
Run Code Online (Sandbox Code Playgroud)

问题是这个功能是CPU密集型的,我不想阻止它.不幸的是,C#库没有Task<Bar> BazAsync()过载.

然后我想通过创建一个调用它并返回(已经启动)的F#函数来自己提供异步版本Async<Bar>.也就是说,我不想用System.Threading.Task<Bar>.

我想我正在寻找的东西相当于Task<T>.Run()F#Async的做事方式.

我已经看过以下选项:

  • Async.StartAsTask - >处理C#ish任务类型.
  • Async.StartAsync.StartImmediately- >收到Async<unit>Async<T>

Async.StartChild我在寻找什么呢?如果是,那将是:

let BazWrapper() =
    let asyncTask: Async<Bar> = async {
        return Foo.Bar()
    }
    Async.StartChild asyncTask
Run Code Online (Sandbox Code Playgroud)

但是,如果以上是解决方案:

  1. 为什么围绕F#异步工作流的大多数文档都没有提到StartChild?
  2. 为什么BazWrapper会返回Async<Async<Bar>>而不是Async<Bar>

rmu*_*unn 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...)
}
Run Code Online (Sandbox Code Playgroud)

当你在async计算表达式中时,let!关键字将"解包"一个Async<'Whatever>离开你的值为type 'Whatever.在调用的情况下Async.StartChild,'Whatever类型是具体的Async<'T>.

因此,如果要返回已启动的异步via Async.StartChild,则执行此操作的方法是:

let BazWrapper() =
    let asyncTask: Async<Bar> = async {
        return Foo.Bar()
    }
    async {
        let! child = Async.StartChild asyncTask
        return! child
    }
Run Code Online (Sandbox Code Playgroud)

但是,我怀疑你会发现已经启动的异步对你来说并不像"冷"异步(还没有启动)那样有用,因为"冷"异步仍然可以与其他异步组合启动之前的异步任务.(这可以用于,例如,包装你的asyncs周围的日志.)所以,如果我正在为你的情况编写代码,我可能只是这样做:

let BazWrapper() =
    async {
        return Foo.Bar()
    }
Run Code Online (Sandbox Code Playgroud)

现在BazWrapper()返回一个尚未启动的Async,Async.RunSynchronously如果您想立即使用该值,或者在其他async计算表达式中使用它,您可以启动它.

  • 与期望任务的C#函数互操作是为什么[`Async.StartAsTask`](https://msdn.microsoft.com/en-us/visualfsharpdocs/conceptual/async.startastask%5B't%5D-method-%5Bfsharp% 5D)存在,它返回一个`Task <'T>`(一个已经启动过的),这是C#代码通常所期望的. (2认同)