当我将Task.Run包装在单独的async / await方法中时抛出TaskCanceledException

时间:2018-09-28 01:31:11

标签: c# task-parallel-library

我有此代码:

//note that this Action is a very time consuming process so I need to wrap it in Task.Run
private static async Task RunBeforeCompletion(Action action)
{

    var task = Task.Run(() =>
    {

        Console.WriteLine("start");
        action();  //some exception could have happened here, or I could have used the Task incorrectly that results in exception
                   //how to catch this exception?

    });
    await task.ContinueWith(t =>
    {
        Console.WriteLine(t.Exception.Message);
        ExceptionDispatchInfo.Capture(t.Exception.InnerException).Throw();
    },
    TaskContinuationOptions.OnlyOnFaulted);
}

private static void Sleep()
{
    Console.WriteLine("sleep");
   Thread.Sleep(1000);
}

private static void RunAll()
{
    var tsk = RunBeforeCompletion(Sleep)
        .ContinueWith(t1 =>
        {
            Console.WriteLine("run");

        });
    Task.WaitAll(tsk);

}

static void Main(string[] args)
{
    RunAll();

}

请注意,Action是一个非常耗时的过程,因此我需要将其包装在Task.Run

哪个工作正常。如果Action

中没有引发异常,则代码可以毫无例外地正常运行

但是,如果我将RunBeforeCompletion的方法主体移到另一个方法,则会抛出TaskCanceledException。即:以下代码将抛出TaskCanceledException

private static async Task WrapTask(Action action)
{
    var task = Task.Run(() =>
    {

        Console.WriteLine("start");
        action();  //some exception could have happened here, or I could have used the Task incorrectly that results in exception
                  //how to catch this exception?

    });
    await task.ContinueWith(t =>
    {
        Console.WriteLine(t.Exception.Message);
        ExceptionDispatchInfo.Capture(t.Exception.InnerException).Throw();

    },
    TaskContinuationOptions.OnlyOnFaulted);
}

private static async Task RunBeforeCompletion(Action action)
{
    await WrapTask(action);
}

private static void Sleep()
{
    Console.WriteLine("sleep");
   Thread.Sleep(1000);
}

private static void RunAll()
{
    var tsk = RunBeforeCompletion(Sleep)
        .ContinueWith(t1 =>
        {
            Console.WriteLine("run");

        });
    Task.WaitAll(tsk);

}

static void Main(string[] args)
{
    RunAll();

}

据我了解,这是因为TaskContinuationOptions.OnlyOnFaulted仅在a single task and not multi-task continuation中有效。

请注意,仅当我在VS 2015中运行带有调试器并带有Exception Settings-> Break的情况下运行第二种情况代码时,才会发生崩溃

如果我在没有调试器的情况下运行,或者如果我不要求在抛出TaskCanceledException时中断VS,则不会有问题。不管是什么,都不应该抛出TaskCanceledException。

第一个问题:

但是第一种和第二种方法不一样吗?我只是将原始Task用另一种方法移动。

我相信我应该始终仅按照准则在单个任务上使用TaskContinuationOptions.OnlyOnFaulted选项,这就是为什么我将其放在Task.Run之后的原因,所以我知道我不会在ContinueWith之后不自觉地将其与其他Task.Run语句链接起来。

这是什么意思?为什么在TaskCanceledException附带调试器的情况下引发但在没有调试器的情况下却不引发?在那种情况下,如何确定所有任务都成功完成?

我要达到什么目的?

action期间,可能会引发某些异常,或者我可能错误地使用了Task更改,所以我想先记录该异常(为此使用Console.WriteLine作为存根玩具示例),然后再将异常抛出以进行进一步处理。这是因为Task.Run内引发的任何异常(或与Task有关的异常)都将被吞没,以后将导致非常神秘的崩溃。所以我想记录异常。

因此,我的第二个问题是,因为如果WrapTask的用户可以在需要的时间内将方法与其他ContinueWith构造链在一起,那么如何编写异常处理代码(用于记录异常错误的目的)优美吗?

0 个答案:

没有答案