Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/335.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么在调用CancellationTokenSource';异步方法中的取消方法是什么?_C#_Asynchronous_Task_Cancellationtokensource_Cancellation Token - Fatal编程技术网

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 for
Task.Run
令牌仅在任务构建期间相关,运行时,您必须自己检查令牌…@andreasniedermar我理解您的意思,但是如果任务完成并且在执行期间的某个时间点调用
CancellationTokenSource.Cancel
,然后它的
task.Result
应该是
TaskStatus.cancelled
不是吗?@AndreasNiedermair如果任务开始执行
task时我理解正确。Status
不会是
TaskStatus.cancelled>即使调用了
Cancel
,我也必须检查
CancellationTokenSource.IsCancellationRequested
var task=task.Run(()=>{token.throwifccancellationRequested();},token)的可能重复项;看起来不错。但在哪里可以捕捉到例外。