C# 为什么在调用CancellationTokenSource';异步方法中的取消方法是什么?
我在C# 为什么在调用CancellationTokenSource';异步方法中的取消方法是什么?,c#,asynchronous,task,cancellationtokensource,cancellation-token,C#,Asynchronous,Task,Cancellationtokensource,Cancellation Token,我在CancellationToken和CancellationTokenSource周围创建了一个小包装器。我遇到的问题是CancellationHelper的CancelAsync方法无法按预期工作 我遇到了方法的问题,它应该throwaexception butstallsinstead方法。要取消正在运行的任务,它将调用await coordinator.CancelAsync(),但该任务实际上并未取消,并且不会在任务上引发异常。请稍候 itworkswell和rowsceceptio
CancellationToken
和CancellationTokenSource
周围创建了一个小包装器。我遇到的问题是CancellationHelper
的CancelAsync
方法无法按预期工作
我遇到了方法的问题,它应该throwaexception butstallsinstead
方法。要取消正在运行的任务,它将调用await coordinator.CancelAsync()代码>,但该任务实际上并未取消,并且不会在任务上引发异常。请稍候
itworkswell和rowsceception
似乎运行良好,它使用了协调器。取消
,这根本不是一种异步方法
在异步方法中调用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);
}
}
}
}
使用系统;
使用System.Collections.Generic;
使用系统线程;
使用System.Threading.Tasks;
命名空间测试取消
{
班级计划
{
静态void Main(字符串[]参数)
{
它可以正常工作,并且具有rowsceception();
//应排除例外,但应暂停();
}
private static void它应该抛弃ExceptionButsStallsinstead()例外
{
Task.Run(异步()=>
{
var协调器=新的CancellationHelper();
var waitHandle=新手动重置事件(错误);
var task=task.Run(()=>
{
waitHandle.WaitOne();
//不过,这很有效——它会抛出
//coordinator.ThrowIfCancellationRequested();
},coordinator.Token);
等待协调器。取消异步();
//waitHandle.Set();--有或没有这个,它都会抛出
task.Wait();
}).Wait();
}
私有静态无效ITWorkswell和RowsException()
{
Task.Run(()=>
{
var协调器=新的CancellationHelper();
var waitHandle=新手动重置事件(错误);
var task=task.Run(()=>{waitHandle.WaitOne();},coordinator.Token);
协调员。取消();
task.Wait();
}).Wait();
}
}
公共类取消助手
{
私有CancellationTokenSource CancellationTokenSource;
私有只读列表任务等待;
公共取消助手()
{
cancellationTokenSource=新的cancellationTokenSource();
tasksToAwait=新列表();
}
公共取消令牌
{
获取{return cancellationTokenSource.Token;}
}
公共作废等待取消(任务)
{
if(task==null)返回;
taskstowait.Add(任务);
}
公共无效重置()
{
taskstowait.Clear();
cancellationTokenSource=新的cancellationTokenSource();
}
请求的公共无效ThrowIfCancellationRequested()
{
cancellationTokenSource.Token.ThrowIfCancellationRequested();
}
公开作废取消()
{
cancellationTokenSource.Cancel();
Task.WaitAll(taskstowait.ToArray());
}
公共异步任务CancelAsync()
{
cancellationTokenSource.Cancel();
尝试
{
wait Task.WhenAll(taskstowait.ToArray());
}
捕获(聚合异常)
{
例如句柄(p=>p为操作取消异常);
}
}
}
}
在.Net中取消是合作的
这意味着持有CancellationTokenSource
的人发出取消信号,持有CancellationToken
的人需要检查是否发出了取消信号(通过轮询CancellationToken
或注册一个代理在发出信号时运行)
在任务.Run
中,您使用取消令牌
作为参数,但不在任务内部检查它,因此只有在任务有机会启动之前发出令牌信号时,任务才会被取消
要在任务运行时取消该任务,您需要检查CancellationToken
:
var task = Task.Run(() =>
{
token.ThrowIfCancellationRequested();
}, token);
在您的情况下,您会阻塞手动重置事件
,因此无法检查取消令牌
。您可以向释放重置事件的CancellationToken
注册委托:
token.Register(() => waitHandle.Set())
您在哪里调用CancellationHelper.ThrowIfCancellationRequested()
?我相信对Cancellation令牌的工作原理存在误解。。。ie forTask.Run
令牌仅在任务构建期间相关,运行时,您必须自己检查令牌…@andreasniedermar我理解您的意思,但是如果任务完成并且在执行期间的某个时间点调用CancellationTokenSource.Cancel
,然后它的task.Result
应该是TaskStatus.cancelled
不是吗?@AndreasNiedermair如果任务开始执行task时我理解正确。Status
不会是TaskStatus.cancelled>即使调用了Cancel
,我也必须检查CancellationTokenSource.IsCancellationRequested
var task=task.Run(()=>{token.throwifccancellationRequested();},token)的可能重复项;看起来不错。但在哪里可以捕捉到例外。