5

Task<T> を使用すると、タスクの実行中に Task.Wait(); 中に例外がスローされます。F# の MailBoxProcessor を使用すると、例外が飲み込まれ、この質問に従って明示的に処理する必要があります。

この違いにより、タスクを介して 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 コードに公開する用途は限られているようです。

4

1 に答える 1