クラスのStop
メソッドを呼び出したくないと仮定すると、ここでは完璧な解決策はありません。TcpListener
操作が特定の時間枠内に完了しなかったときに通知を受けても、元の操作を完了できる場合は、次のように拡張メソッドを作成できます。
public static async Task<T> WithWaitCancellation<T>(
this Task<T> task, CancellationToken cancellationToken)
{
// The tasck completion source.
var tcs = new TaskCompletionSource<bool>();
// Register with the cancellation token.
using(cancellationToken.Register( s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs) )
{
// If the task waited on is the cancellation token...
if (task != await Task.WhenAny(task, tcs.Task))
throw new OperationCanceledException(cancellationToken);
}
// Wait for one or the other to complete.
return await task;
}
上記は、StephenToubのブログ投稿「キャンセルできない非同期操作をキャンセルするにはどうすればよいですか?」からのものです。。
ここでの警告は繰り返しになりますが、これは実際には操作をキャンセルしません。これは、をとるAcceptTcpClientAsync
メソッドの過負荷がないため、キャンセルすることができCancellationToken
ないためです。
つまり、拡張メソッドがキャンセルが発生したことを示している場合、操作自体をキャンセルするのTask
ではなく、元のコールバックの待機をキャンセルしていることになります。
そのために、実際のアクションではなく、待機をキャンセルすることを示すために、メソッドの名前をからWithCancellation
に変更しました。WithWaitCancellation
そこから、コードで簡単に使用できます。
// Create the listener.
var tcpListener = new TcpListener(connection);
// Start.
tcpListener.Start();
// The CancellationToken.
var cancellationToken = ...;
// Have to wait on an OperationCanceledException
// to see if it was cancelled.
try
{
// Wait for the client, with the ability to cancel
// the *wait*.
var client = await tcpListener.AcceptTcpClientAsync().
WithWaitCancellation(cancellationToken);
}
catch (AggregateException ae)
{
// Async exceptions are wrapped in
// an AggregateException, so you have to
// look here as well.
}
catch (OperationCancelledException oce)
{
// The operation was cancelled, branch
// code here.
}
OperationCanceledException
待機がキャンセルされた場合にスローされたインスタンスをキャプチャするには、クライアントの呼び出しをラップする必要があることに注意してください。
非同期操作からスローされると例外がラップされるため、キャッチもスローしましたAggregateException
(この場合は自分でテストする必要があります)。
Stop
それは、もちろん、あなたの状況に依存する方法のような方法(基本的に、何が起こっているかに関係なく、すべてを激しく破壊するもの)を持っていることに直面して、どちらのアプローチがより良いアプローチであるかという問題を残します。
待機しているリソース(この場合はTcpListener
)を共有していない場合は、リソースを使用してabortメソッドを呼び出し、待機している操作から発生する例外を飲み込む方がよいでしょう( stopを呼び出し、操作を待機している他の領域でそのビットを監視するときに、少し反転する必要があります)。これにより、コードがいくらか複雑になりますが、リソースの使用率とできるだけ早くクリーンアップすることに関心があり、この選択が利用できる場合は、これが最適な方法です。
リソースの使用率が問題ではなく、より協調的なメカニズムに慣れていて、リソースを共有していないWithWaitCancellation
場合は、この方法を使用しても問題ありません。ここでの長所は、コードがよりクリーンで、保守が容易なことです。