为什么从Task抛出的异常被视为已取消?

时间:2019-06-01 16:02:24

标签: c#

为什么从Task引发的异常被视为已取消(即使我没有按'x'来取消它)却没有被视为有故障? 以下代码的输出是:

Press 'x' to cancel
Job has been canceled
Task has been canceled
Press Enter to exit

代码

static void Main(string[] args)
    {
        try
        {   
            var cancellationTokenSource = new CancellationTokenSource();
            var cancellationToken = cancellationTokenSource.Token;

            var jobTask = Task.Run(() =>
            {
                if (cancellationToken.IsCancellationRequested)
                    cancellationToken.ThrowIfCancellationRequested();

                while (true)
                {
                    if (cancellationToken.IsCancellationRequested)
                        cancellationToken.ThrowIfCancellationRequested();

                    Thread.Sleep(2000);

                    if (cancellationToken.IsCancellationRequested)
                        cancellationToken.ThrowIfCancellationRequested();

                    throw new Exception("Test Exception");
                }

            }, cancellationToken)
            .ContinueWith((t) =>
            {
                Console.WriteLine("Job has been completed");
            }, TaskContinuationOptions.OnlyOnRanToCompletion)
            .ContinueWith((t) =>
            {
                Console.WriteLine("Job has been canceled");
            }, TaskContinuationOptions.OnlyOnCanceled)
            .ContinueWith((t) =>
                {
                    Console.WriteLine("Exception thrown: {0}", t.Exception.InnerException);
                }, TaskContinuationOptions.OnlyOnFaulted);

            Task.Run(() =>
            {
                Console.WriteLine("Press 'x' to cancel");
                while (Console.ReadKey(true).KeyChar != 'x')
                {
                    Thread.Sleep(200);
                }

                cancellationTokenSource.Cancel();
            });

            try
            {
                jobTask.Wait();
            }
            catch (AggregateException ex)
            {
                foreach (var v in ex.InnerExceptions)
                {
                    if (v is TaskCanceledException)
                        Console.WriteLine("Task has been canceled");
                    else
                        Console.WriteLine("Exception: {0} - {1}", v.GetType().Name, v.InnerException);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception: {0}", ex.Message);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("E - {0}", ex.Message);
        }

        Console.WriteLine("Press Enter to exit");
        Console.ReadKey();
    }
}

1 个答案:

答案 0 :(得分:1)

Task.ContinueWith方法返回一个代表继续的新Task对象。如果不满足continuationOptions参数中指定的条件,则取消继续任务。

您的代码将对continueWith的三个调用链接在一起。这实际上创建了四个任务:

  • Task用于传递给Task.Run的委托执行,两秒钟后引发异常
  • 仅在第一个Task成功完成后才运行的第一个延续Task
  • 仅在第一个延续被取消的情况下运行的第二个延续Task
  • 仅在第二个延续出现故障时才运行的第三个延续Task

引发异常时,第一个Task错误,第一个继续被取消,因为它继续执行的Task未成功完成,第二个继续运行,因为第一个继续被取消,并且第三个延续被取消,因为第二个延续没有错。

try块正在Wait上调用jobTask,该Task保留了第三连续的public interface ProgrammationRepository extends JpaRepository<Programmation, Long> { @Query("select programmation from Programmation programmation left join fetch programmation.film where programmation.dateprogrammation >= ?#{@ProgrammationRepository.addSixDay()} and programmation.dateprogrammation <= ?#{@ProgrammationRepository.addSevenDay()}") List<Programmation> getSevenNextDay(); @Query("select programmation from Programmation programmation left join fetch programmation.film where programmation.film.id = :id") List<Programmation> getFindByFilm(@Param("id") Long id); default Instant addSixDay() { System.out.println( Instant.now().plus(6, ChronoUnit.DAYS)); return Instant.now().minus(6, ChronoUnit.DAYS); } default Instant addSevenDay() { return Instant.now().plus(7, ChronoUnit.DAYS); } ,该连续总是被取消。

Microsoft文档参考:Task.ContinueWith