繁体   English   中英

统一任务 <T> 和F#MailboxProcessor异常处理

[英]Unifying Task<T> and F# MailboxProcessor exception handling

使用Task <T>时,在Task.Wait()期间抛出任务执行期间的异常; 当使用F#的MailBoxProcessor时,异常会被吞下,需要根据这个问题明确处理。

这种差异使得很难通过Task将F#代理暴露给C#代码。 例如,此代理:

type internal IncrementMessage = 
    Increment of int * AsyncReplyChannel<int>

type IncrementAgent() =
    let counter = Agent.Start(fun agent -> 
        let rec loop() = async { let! Increment(msg, replyChannel) = agent.Receive()
                                match msg with 
                                | int.MaxValue -> return! failwith "Boom!"
                                | _ as i -> replyChannel.Reply (i + 1)
                                            return! loop() }

        loop())

    member x.PostAndAsyncReply i =
        Async.StartAsTask (counter.PostAndAsyncReply (fun channel -> Increment(i, channel)))

可以从C#调用,但异常不会返回给C#:

[Test]
public void ExceptionHandling()
{
    //
    // TPL exception behaviour
    //
    var task = Task.Factory.StartNew<int>(() => { throw new Exception("Boom!"); });

    try
    {
        task.Wait();
    }
    catch(AggregateException e)
    {
        // Exception available here
        Console.WriteLine("Task failed with {0}", e.InnerException.Message);
    }

    //
    // F# MailboxProcessor exception behaviour
    //
    var incAgent = new IncrementAgent();
    task = incAgent.PostAndAsyncReply(int.MaxValue);

    try
    {
        task.Wait(); // deadlock here
    }
    catch (AggregateException e)
    {
        Console.WriteLine("Agent failed with {0}", e.InnerException.Message);
    }
}

C#代码只挂起在task.Wait()而不是获得异常。 有没有办法让F#代理行为像任务? 如果没有,似乎将F#代理暴露给其他.NET代码的用途有限。

处理它的一种方法是让代理返回带有错误情况的DU。 然后,您可以从代理外部引发异常。

type internal IncrementResponse =
    | Response of int
    | Error of exn

type internal IncrementMessage = 
    | Increment of int * AsyncReplyChannel<IncrementResponse>

type IncrementAgent() =
    let counter = Agent.Start(fun agent -> 
        let rec loop() = 
          async { 
            let! Increment(msg, replyChannel) = agent.Receive()
            match msg with 
            | int.MaxValue -> replyChannel.Reply (Error (Failure "Boom!"))
            | _ as i -> replyChannel.Reply (Response(i + 1))
            return! loop() 
          }
        loop())

    member x.PostAndAsyncReply i =
        Async.StartAsTask (
          async {
            let! res = counter.PostAndAsyncReply (fun channel -> Increment(i, channel))
            match res with
            | Response i -> return i
            | Error e -> return (raise e)
          }
        )

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM