[英]Unifying Task<T> and F# MailboxProcessor exception handling
When using Task<T>, an exception during the execution of the task is thrown during Task.Wait(); 使用Task <T>时,在Task.Wait()期间抛出任务执行期间的异常; when using F#'s MailBoxProcessor, the exception gets swallowed and needs to be explicitly dealt with as per this question . 当使用F#的MailBoxProcessor时,异常会被吞下,需要根据这个问题明确处理。
This difference makes it difficult to expose F# agents to C# code via a Task. 这种差异使得很难通过Task将F#代理暴露给C#代码。 For example, this agent: 例如,此代理:
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)))
can be called from C#, but the exception is not returned to C#: 可以从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);
}
}
Instead of getting the exception, the C# code just hangs at task.Wait(). C#代码只挂起在task.Wait()而不是获得异常。 Is there any way to get the F# agent to behave like a Task? 有没有办法让F#代理行为像任务? If not, it seems like there is limited use in exposing F# agents to other .NET code. 如果没有,似乎将F#代理暴露给其他.NET代码的用途有限。
One way to handle it is have the agent return a DU with an error case. 处理它的一种方法是让代理返回带有错误情况的DU。 You could then raise the exception from outside the agent. 然后,您可以从代理外部引发异常。
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.