3

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

4

2 に答える 2

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の組み込みキャンセル機能 (もちろん に基づいています) を使用できます。(つまり、キャンセル目的で電話するべきではありません。)CancellationTokenCancellationTokenSocket.Close

于 2012-07-12T02:50:37.217 に答える
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)

これはまだ私の関数を使用して、例外を除外して無視します (ネットワーキングに関しては、例外は非常に例外的ではないと思います。このアプリの場合、とにかく応答して何もしません)。

于 2012-07-11T16:45:52.300 に答える