我正在使用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魔法?
答案 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)
这仍然使用我的函数来过滤掉并忽略异常(我不认为异常在网络方面是非常特殊的,在这个应用程序的情况下,无论如何都没有任何反应)。