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 コードに公開する用途は限られているようです。