假设您不想调用Stop method http://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.stop.aspx on the TcpListener class http://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.aspx,这里没有完美的解决方案。
如果您愿意在操作未在特定时间范围内完成时收到通知,但允许原始操作完成,那么您可以创建一个扩展方法,如下所示:
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;
}
以上内容来自Stephen Toub 的博客文章“如何取消不可取消的异步操作?” http://blogs.msdn.com/b/pfxteam/archive/2012/10/05/how-do-i-cancel-non-cancelable-async-operations.aspx.
这里的警告值得重复,这实际上并不cancel操作,因为没有过载AcceptTcpClientAsync method http://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.accepttcpclientasync.aspx这需要一个CancellationToken http://msdn.microsoft.com/en-us/library/system.threading.cancellationtoken.aspx, 它不是able被取消。
这意味着如果扩展方法指示取消did发生这种情况,您正在取消对原始回调的等待Task http://msdn.microsoft.com/en-us/library/system.threading.tasks.task.aspx, not取消操作本身。
为此,这就是为什么我将该方法重命名为WithCancellation
to WithWaitCancellation
表明您正在取消wait,而不是实际行动。
从那里,它很容易在您的代码中使用:
// 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 http://msdn.microsoft.com/en-us/library/system.operationcanceledexception.aspx如果取消等待则抛出实例。
我还扔了一个AggregateException http://msdn.microsoft.com/en-us/library/system.aggregateexception.aspxcatch 作为从异步操作抛出异常时被包装的异常(在这种情况下您应该自己测试)。
这就留下了一个问题:面对像这样的方法,哪种方法是更好的方法Stop method http://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.stop.aspx(基本上,任何暴力地摧毁一切的东西,无论发生什么),当然,这取决于你的情况。
如果您不共享正在等待的资源(在本例中,TcpListener
),那么调用 abort 方法并吞下来自您正在等待的操作的任何异常可能是更好的资源利用方式(当您调用 stop 并在另一个中监视该位时,您必须稍微翻转一下)您正在等待手术的区域)。这会增加代码的复杂性,但如果您担心资源利用率和尽快清理,并且您可以选择此选项,那么这就是正确的选择。
如果资源利用率为not一个问题,并且您对更加合作的机制感到满意,并且您not共享资源,然后使用WithWaitCancellation
方法很好。这里的优点是它的代码更干净,并且更易于维护。