问题描述
什么是取消以下正确的方法是什么?
What is the correct way to cancel the following?
var tcpListener = new TcpListener(connection);
tcpListener.Start();
var client = await tcpListener.AcceptTcpClientAsync();
简单地调用 tcpListener.Stop()
似乎导致的ObjectDisposedException
和 AcceptTcpClientAsync
方法不接受的CancellationToken
结构。
Simply calling tcpListener.Stop()
seems to result in an ObjectDisposedException
and the AcceptTcpClientAsync
method doesn't accept a CancellationToken
structure.
我是完全失去了一些东西明显?
Am I totally missing something obvious?
推荐答案
假设你不想叫<$c$c>Stop$c$c>在 href=\"http://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.aspx\"><$c$c>TcpListener$c$c>类,这里有没有完美的解决方案。
Assuming that you don't want to call the Stop
method on the TcpListener
class, there's no perfect solution here.
如果你没事带被通知当操作未在规定时间内完成,但允许原来的操作来完成,那么你可以创建一个扩展方法,像这样:
If you're alright with being notified when the operation doesn't complete within a certain time frame, but allowing the original operation to complete, then you can create an extension method, like so:
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的博客文章我怎么取消不取消异步操作?。
在这里需要注意的重复承担,这实际上并不取消的操作,因为没有的<$c$c>AcceptTcpClientAsync$c$c>这需要一个 href=\"http://msdn.microsoft.com/en-us/library/system.threading.cancellationtoken.aspx\"><$c$c>CancellationToken$c$c>,它不是的能够的取消。
The caveat here bears repeating, this doesn't actually cancel the operation, because there is not an overload of the AcceptTcpClientAsync
method that takes a CancellationToken
, it's not able to be cancelled.
这意味着,如果扩展方法指示取消的没有的发生,你要取消对原有的回调<$c$c>Task$c$c>,的不的取消操作本身。
That means that if the extension method indicates that a cancellation did happen, you are cancelling the wait on the callback of the original Task
, not cancelling the operation itself.
为此,这就是为什么我从改名 WithCancellation
的方法 WithWaitCancellation
来表明您被取消的等的,而不是实际的行动。
To that end, that is why I've renamed the method from WithCancellation
to WithWaitCancellation
to indicate that you are cancelling the wait, not the actual action.
从那里,很容易在你的code使用方法:
From there, it's easy to use in your code:
// 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.
}
请注意,您必须包装呼叫您的客户端来捕捉<$c$c>OperationCanceledException$c$c>如果等待被取消实例抛出。
Note that you'll have to wrap the call for your client to capture the OperationCanceledException
instance thrown if the wait is cancelled.
我也扔在<$c$c>AggregateException$c$c>赶上从异步操作(你应该在这种情况下,测试自己)抛出异常时包裹。
I've also thrown in an AggregateException
catch as exceptions are wrapped when thrown from asynchronous operations (you should test for yourself in this case).
剩下的问题,其中的方法是在具有类似<$c$c>Stop$c$c>方法(基本上,任何事情猛烈眼泪都记录下来,不管是怎么回事),这当然取决于你的情况。
That leaves the question of which approach is a better approach in the face of having a method like the Stop
method (basically, anything which violently tears everything down, regardless of what is going on), which of course, depends on your circumstances.
如果你不分享你正在等待(在这种情况下,的TcpListener
)的资源,那么它很可能是一个更好地利用资源调用中止方法和吞下来自操作的任何异常你等待(你必须翻转了一下,当你调用停止和监控你在操作等待其他地区该位)。这增加了一些复杂的code,但如果你尽快很关心资源利用和清理,并且这种选择是提供给你,那么这是要走的路。
If you are not sharing the resource that you're waiting on (in this case, the TcpListener
), then it would probably be a better use of resources to call the abort method and swallow any exceptions that come from operations you're waiting on (you'll have to flip a bit when you call stop and monitor that bit in the other areas you're waiting on an operation). This adds some complexity to the code but if you're concerned about resource utilization and cleaning up as soon as possible, and this choice is available to you, then this is the way to go.
如果资源利用率的不的一个问题,您认为合适的更多的合作机制,而你的不的共享资源,然后使用 WithWaitCancellation
办法是罚款。这里的优点是它的清洁code和更易于维护。
If resource utilization is not an issue and you're comfortable with a more cooperative mechanism, and you're not sharing the resource, then using the WithWaitCancellation
method is fine. The pros here are that it's cleaner code, and easier to maintain.
这篇关于什么是取消异步操作,不接受的CancellationToken的正确方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!