我有此代码:
//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
构造链在一起,那么如何编写异常处理代码(用于记录异常错误的目的)优美吗?