【发布时间】:2015-06-22 08:47:25
【问题描述】:
我围绕CancellationToken 和CancellationTokenSource 创建了一个小包装器。我遇到的问题是CancellationHelper 的CancelAsync 方法没有按预期工作。
我遇到了ItShouldThrowAExceptionButStallsInstead 方法的问题。取消正在运行的任务,它调用await coordinator.CancelAsync();,但该任务实际上并没有被取消,也不会在task.Wait上抛出异常
ItWorksWellAndThrowsException 似乎运行良好,它使用coordinator.Cancel,这根本不是异步方法。
问题为什么我在异步方法中调用CancellationTokenSource的Cancel方法时任务没有取消?
不要让waitHandle迷惑你,这只是为了不让任务提前完成。
让代码自己说话:
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace TestCancellation
{
class Program
{
static void Main(string[] args)
{
ItWorksWellAndThrowsException();
//ItShouldThrowAExceptionButStallsInstead();
}
private static void ItShouldThrowAExceptionButStallsInstead()
{
Task.Run(async () =>
{
var coordinator = new CancellationHelper();
var waitHandle = new ManualResetEvent(false);
var task = Task.Run(() =>
{
waitHandle.WaitOne();
//this works well though - it throws
//coordinator.ThrowIfCancellationRequested();
}, coordinator.Token);
await coordinator.CancelAsync();
//waitHandle.Set(); -- with or without this it will throw
task.Wait();
}).Wait();
}
private static void ItWorksWellAndThrowsException()
{
Task.Run(() =>
{
var coordinator = new CancellationHelper();
var waitHandle = new ManualResetEvent(false);
var task = Task.Run(() => { waitHandle.WaitOne(); }, coordinator.Token);
coordinator.Cancel();
task.Wait();
}).Wait();
}
}
public class CancellationHelper
{
private CancellationTokenSource cancellationTokenSource;
private readonly List<Task> tasksToAwait;
public CancellationHelper()
{
cancellationTokenSource = new CancellationTokenSource();
tasksToAwait = new List<Task>();
}
public CancellationToken Token
{
get { return cancellationTokenSource.Token; }
}
public void AwaitOnCancellation(Task task)
{
if (task == null) return;
tasksToAwait.Add(task);
}
public void Reset()
{
tasksToAwait.Clear();
cancellationTokenSource = new CancellationTokenSource();
}
public void ThrowIfCancellationRequested()
{
cancellationTokenSource.Token.ThrowIfCancellationRequested();
}
public void Cancel()
{
cancellationTokenSource.Cancel();
Task.WaitAll(tasksToAwait.ToArray());
}
public async Task CancelAsync()
{
cancellationTokenSource.Cancel();
try
{
await Task.WhenAll(tasksToAwait.ToArray());
}
catch (AggregateException ex)
{
ex.Handle(p => p is OperationCanceledException);
}
}
}
}
【问题讨论】:
-
你在哪里打电话
CancellationHelper.ThrowIfCancellationRequested()? -
我相信对canellation令牌的工作方式存在误解......即对于
Task.Run,令牌仅在任务构建期间相关,运行时,您必须自己检查令牌.. . -
@AndreasNiedermair 我明白你在说什么,但是如果任务完成并且在执行过程中的某个时刻调用了
CancellationTokenSource.Cancel,那么它的task.Result应该是TaskStatus.Canceled不应该吗? -
@AndreasNiedermair 如果我理解正确,一旦任务开始执行
task.Status就不会是TaskStatus.Canceled,即使Cancel被调用,我必须检查CancellationTokenSource.IsCancellationRequested
标签: c# asynchronous task cancellationtokensource cancellation-token