我正在使用 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() 方法的挂起调用,请关闭 Socket。当异步操作正在进行时调用 Close() 方法时,将调用提供给 BeginAccept() 方法的回调。对 EndAccept() 方法的后续调用将引发 ObjectDisposedException 以指示操作已被取消。
我仍然不喜欢这个例外,即使它是设计使然。我宁愿找到一种不在已处置对象上调用 EndXXX() 的方法。也许我可以以某种方式将一些 CancellationToken 魔法混入其中?