我在CancellationToken
和CancellationTokenSource
周圍創建了一個小包裝。我的問題是CancelAsync
方法CancellationHelper
不能按預期方式工作。當我在異步方法中調用CancellationTokenSource的Cancel方法時,爲什麼不取消任務?
我遇到了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 token的工作方式存在誤解......即對於'Task.Run'這個令牌只有在任務建立時纔有意義,運行時必須自己檢查令牌... –
@AndreasNiedermair I明白你在說什麼,但是如果任務完成,並且在執行過程中的某個時刻調用CancellationTokenSource.Cancel,那麼它的task.Result應該是TaskStatus.Canceled ''不應該嗎? – boli