gpt4 book ai didi

c# - 取消不接受 CancellationToken 的异步操作的正确方法是什么?

转载 作者:可可西里 更新时间:2023-11-01 02:53:54 25 4
gpt4 key购买 nike

取消下面的正确方法是什么?

var tcpListener = new TcpListener(connection);
tcpListener.Start();
var client = await tcpListener.AcceptTcpClientAsync();

简单地调用 tcpListener.Stop() 似乎会导致 ObjectDisposedExceptionAcceptTcpClientAsync 方法不接受 CancellationToken 结构。

我是否完全遗漏了一些明显的东西?

最佳答案

假设您不想调用 Stop methodTcpListener class 上,这里没有完美的解决方案。

如果您不介意在特定时间范围内操作未完成时收到通知,但允许原始操作完成,那么您可以创建一个扩展方法,如下所示:

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's blog post "How do I cancel non-cancelable async operations?" .

这里需要重复的警告,这实际上并没有取消操作,因为没有重载AcceptTcpClientAsync method。这需要 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 catch,因为当从异步操作抛出异常时,异常会被包装(在这种情况下,您应该自己测试)。

这就留下了一个问题,面对像 Stop method 这样的方法,哪种方法更好。 (基本上,无论发生什么,都会猛烈地破坏一切),当然,这取决于您的情况。

如果您不共享您正在等待的资源(在本例中为 TcpListener),那么调用 abort 方法并吞下任何资源可能会更好来自您正在等待的操作的异常(当您调用停止并在您正在等待操作的其他区域监视该位时,您必须稍微翻转)。这给代码增加了一些复杂性,但如果您关心资源利用率和尽快清理,并且您可以选择,那么这就是方法。

如果资源利用率不是问题,并且您愿意使用更合作的机制,并且您共享资源,那么使用 WithWaitCancellation 方法很好。这里的优点是它的代码更清晰,更易于维护。

关于c# - 取消不接受 CancellationToken 的异步操作的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40504418/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com