F#Async.FromBeginEnd什么时候不应该调用End函数?

时间:2012-07-11 13:40:12

标签: asynchronous f# io

我正在使用F#进行一些低级套接字工作,并使所有内容异步。我有一个套接字,我用来监听连接使用异步工作流来处理它们,所以它使用它来包装Socket.BeginListen()和Socket.EndListen()。

member socket.AsyncAccept () =
    Async.FromBeginEnd( socket.BeginAccept, endOrDisposed socket.EndAccept null )

当我想停止收听时,此刻我正在对其执行Socket.Close(),这将导致Socket.BeginAccept()启动的异步操作完成。

最初我遇到了一个问题,因为无论BeginXXX()的操作如何完成,FromBeginEnd()函数总是会调用EndXXX()函数。在某些情况下,这将导致EndXXX()函数的ObjectDisposed异常,就像它被调用Socket已被关闭和处理时一样。我添加了一个小的处理函数,可以过滤掉那些异常:

let endOrDisposed endFunc defaultResult iar = try endFunc iar with | _ -> defaultResult

这样可以解决问题,但在调试中运行则不行。我很清楚Just My Code选项可以用来隐藏异常,但是这可能会在更频繁的基础上发生在其他一些IO操作上,所以我也不想浪费处理器时间来提高和捕获异常真的是在那里。此外,我可能不想隐藏其他异常被抛出的地方,因为它们可能需要调试。

我已经查看了Async.FromBeginEnd的代码,无论如何都连接到始终调用EndXXX()函数,我不确定这是最好的行为,也许我应该为它写一个替代品?或者是否有人对优雅的解决方案有任何其他想法?


我刚刚在文档中找到了(确定我之前已经看过了):

  

要取消对BeginAccept()方法的挂起调用,请关闭   插座。在异步时调用Close()方法时   操作正在进行中,提供给BeginAccept()的回调   方法被调用。随后对EndAccept()方法的调用将会   抛出ObjectDisposedException以指示操作具有   被取消了。

我仍然不喜欢这个例外,即使它是设计的。我更愿意找到一种不在被处置对象上调用EndXXX()的方法。也许我可以在某种程度上混合一些CancellationToken魔法?

2 个答案:

答案 0 :(得分:1)

我会在cancelAction处使用可选的Async.FromBeginEnd参数:

type Socket with
  member this.AsyncAccept () =
    let canceled = ref false
    let endAccept iar = if not !canceled then this.EndAccept iar else null
    let cancel () = canceled := true; this.Close ()
    Async.FromBeginEnd (this.BeginAccept, endAccept, cancelAction = cancel)

这样您就可以使用Async的内置取消功能(当然基于CancellationToken),而无需直接触及CancellationToken。 (即,您应该致电Socket.Close进行取消。)

答案 1 :(得分:0)

需要一种方法来中断Async.FromBeginEnd进程并在套接字被关闭的情况下阻止它尝试EndAccept(这可能会扩展到除Accept之外的其他操作)。

我没有调用Socket.Close(),而是使用了CancellationTokenSource并使用它注册了一个处理程序,它将调用Socket.Close():

cts.Token.Register (fun () -> listener.Close()) |> ignore

然后不是直接传递Async.FromBeginEnd EndAccept函数,而是通过一个函数来停止在令牌被取消时调用EndAccept。

let endOrDisposed endFunc defaultResult iar = try endFunc iar with | _ -> defaultResult
let endIfNotCancelled endFunc defaultResult (token:CancellationToken) iar = if token.IsCancellationRequested then defaultResult else endOrDisposed endFunc defaultResult iar

type Socket with
  member socket.AsyncAccept (cancelToken) =
    Async.FromBeginEnd( socket.BeginAccept, endIfNotCancelled socket.EndAccept null cancelToken)

这仍然使用我的函数来过滤掉并忽略异常(我不认为异常在网络方面是非常特殊的,在这个应用程序的情况下,无论如何都没有任何反应)。