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 マジックをこれに混ぜることができますか?