C# 取消不带';你不接受取消代币吗?

C# 取消不带';你不接受取消代币吗?,c#,.net,asynchronous,task-parallel-library,async-await,C#,.net,Asynchronous,Task Parallel Library,Async Await,取消以下内容的正确方法是什么 var tcpListener = new TcpListener(connection); tcpListener.Start(); var client = await tcpListener.AcceptTcpClientAsync(); 简单地调用tcpListener.Stop()似乎会导致ObjectDisposedException并且AcceptCpclientAsync方法不接受CancellationToken结构 我是不是完全忽略了一些显而易

取消以下内容的正确方法是什么

var tcpListener = new TcpListener(connection);
tcpListener.Start();
var client = await tcpListener.AcceptTcpClientAsync();
简单地调用
tcpListener.Stop()
似乎会导致
ObjectDisposedException
并且
AcceptCpclientAsync
方法不接受
CancellationToken
结构


我是不是完全忽略了一些显而易见的东西?

假设你不想打电话,这里没有完美的解决方案

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

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; 
}
请注意,如果取消了等待,您必须为客户端包装调用以捕获抛出的实例

我还抛出了一个catch,因为当从异步操作抛出异常时,异常会被包装(在本例中,您应该自己测试)

这就留下了一个问题,那就是在面对像这样的方法时,哪种方法是更好的方法(基本上,任何东西都会猛烈地撕裂一切,不管发生了什么),当然,这取决于你的情况

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


如果资源利用率不是一个问题,并且您对一种更具协作性的机制感到满意,并且您没有共享资源,那么使用
WithWaitCancellation
方法就可以了。这里的优点是它的代码更干净,维护更容易。

假设您不想在上调用,那么就没有完美的解决方案在这里

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

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; 
}
请注意,如果取消了等待,您必须为客户端包装调用以捕获抛出的实例

我还抛出了一个catch,因为当从异步操作抛出异常时,异常会被包装(在本例中,您应该自己测试)

这就留下了一个问题,那就是在面对像这样的方法时,哪种方法是更好的方法(基本上,任何东西都会猛烈地撕裂一切,不管发生了什么),当然,这取决于你的情况

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


如果资源利用率不是一个问题,并且您对一种更具协作性的机制感到满意,并且您没有共享资源,那么使用
WithWaitCancellation
方法就可以了。这里的优点是它的代码更干净,维护更容易。

casperOne的答案是正确的,但有更干净的潜在实现使用实现相同目标的
with cancellation
(或
with wait cancellation
)扩展方法:

static Task<T> WithCancellation<T>(this Task<T> task, CancellationToken cancellationToken)
{
    return task.IsCompleted
        ? task
        : task.ContinueWith(
            completedTask => completedTask.GetAwaiter().GetResult(),
            cancellationToken,
            TaskContinuationOptions.ExecuteSynchronously,
            TaskScheduler.Default);
}
取消的静态任务(此任务任务,取消令牌取消令牌)
{
return task.IsCompleted
任务
:task.ContinueWith(
completedTask=>completedTask.GetAwaiter().GetResult(),
取消令牌,
TaskContinuationOptions.Executes同步执行,
TaskScheduler.Default);
}
  • 首先,我们通过检查任务是否已经完成来进行快速路径优化
  • 然后,我们只需注册原始任务的一个延续,并传递
    CancellationToken
    参数
  • 如果可能,继续将同步提取原始任务的结果(或异常,如果存在异常)(
    TaskContinuationOptions.executes同步执行
    ),如果不同步,则使用
    ThreadPool
    线程(
    TaskScheduler.Default
    ),同时观察
    取消令牌

如果原始任务在取消
CancellationToken
之前完成,则返回的任务将存储结果,否则该任务将被取消,并在等待时抛出
TaskCancelledException

虽然casperOne的答案正确,但使用取消
具有更清晰的潜在实现>(或
WithWaitCancellation
)实现相同目标的扩展方法:

static Task<T> WithCancellation<T>(this Task<T> task, CancellationToken cancellationToken)
{
    return task.IsCompleted
        ? task
        : task.ContinueWith(
            completedTask => completedTask.GetAwaiter().GetResult(),
            cancellationToken,
            TaskContinuationOptions.ExecuteSynchronously,
            TaskScheduler.Default);
}
取消的静态任务(此任务任务,取消令牌取消令牌)
{
return task.IsCompleted
任务
:task.ContinueWith(
completedTask=>completedTask.GetAwaiter().GetResult(),
取消令牌,
TaskContinuationOptions.Executes同步执行,
TaskScheduler.Default);
}
  • 首先,我们通过检查任务是否已经完成来进行快速路径优化
  • 然后,我们只需注册原始任务的一个延续,并传递
    CancellationToken
    参数
  • 继续部分提取原始任务的结果