bol*_*oli 6 c# asynchronous task cancellationtokensource cancellation-token
我创造了一个小包装CancellationToken和CancellationTokenSource.我遇到的问题是CancelAsync方法CancellationHelper不能按预期工作.
我遇到了这个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);
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
.Net中的取消是合作的.
这意味着持有CancellationTokenSource信号取消的那个和CancellationToken需要检查取消是否被发信号的那个(通过轮询CancellationToken或通过在发信号时注册代表来运行).
在您Task.Run使用CancellationTokenas作为参数时,您不在任务本身内部检查它,因此只有在任务有机会启动之前发出令牌信号才会取消该任务.
要在任务运行时取消该任务,您需要检查CancellationToken:
var task = Task.Run(() =>
{
token.ThrowIfCancellationRequested();
}, token);
Run Code Online (Sandbox Code Playgroud)
在你的情况下你阻止了,ManualResetEvent所以你将无法检查CancellationToken.您可以将代理注册到CancellationToken释放重置事件的代理:
token.Register(() => waitHandle.Set())
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4803 次 |
| 最近记录: |