Eam*_*mon 31 c# exception-handling task-parallel-library
当我需要能够取消大型/长期运行工作负载的任务时,我经常使用与此类似的模板执行任务:
public void DoWork(CancellationToken cancelToken)
{
try
{
//do work
cancelToken.ThrowIfCancellationRequested();
//more work
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception ex)
{
Log.Exception(ex);
throw;
}
}
Run Code Online (Sandbox Code Playgroud)
不应将OperationCanceledException记录为错误,但如果任务要转换为已取消状态,则不得吞下OperationCanceledException.除了此方法的范围之外,不需要处理任何其他异常.
这总觉得有点笨重,默认情况下visual studio会在OperationCanceledException的中断(虽然因为我使用了这种模式,我已经'关闭了User-unhandled'现在关闭了OperationCanceledException).
理想情况下,我认为我希望能够做到这样的事情:
public void DoWork(CancellationToken cancelToken)
{
try
{
//do work
cancelToken.ThrowIfCancellationRequested();
//more work
}
catch (Exception ex) exclude (OperationCanceledException)
{
Log.Exception(ex);
throw;
}
}
Run Code Online (Sandbox Code Playgroud)
即将某种排除列表应用于捕获但没有当前不可能的语言支持(@ eric-lippert:c#vNext feature :)).
另一种方式是通过延续:
public void StartWork()
{
Task.Factory.StartNew(() => DoWork(cancellationSource.Token), cancellationSource.Token)
.ContinueWith(t => Log.Exception(t.Exception.InnerException), TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously);
}
public void DoWork(CancellationToken cancelToken)
{
//do work
cancelToken.ThrowIfCancellationRequested();
//more work
}
Run Code Online (Sandbox Code Playgroud)
但我真的不喜欢这样,因为异常在技术上可能只有一个内部异常而且你在记录异常时没有像第一个例子那样多的上下文(如果我做的不仅仅是记录它).
我理解这是一个风格问题,但想知道是否有人有更好的建议?
我只需坚持示例1吗?
埃蒙
Den*_*nis 15
所以有什么问题?扔掉catch (OperationCanceledException)
街区,并设置适当的延续:
var cts = new CancellationTokenSource();
var task = Task.Factory.StartNew(() =>
{
var i = 0;
try
{
while (true)
{
Thread.Sleep(1000);
cts.Token.ThrowIfCancellationRequested();
i++;
if (i > 5)
throw new InvalidOperationException();
}
}
catch
{
Console.WriteLine("i = {0}", i);
throw;
}
}, cts.Token);
task.ContinueWith(t =>
Console.WriteLine("{0} with {1}: {2}",
t.Status,
t.Exception.InnerExceptions[0].GetType(),
t.Exception.InnerExceptions[0].Message
),
TaskContinuationOptions.OnlyOnFaulted);
task.ContinueWith(t =>
Console.WriteLine(t.Status),
TaskContinuationOptions.OnlyOnCanceled);
Console.ReadLine();
cts.Cancel();
Console.ReadLine();
Run Code Online (Sandbox Code Playgroud)
TPL区分取消和错误.因此,取消(即OperationCancelledException
在任务主体内投掷)不是一个错误.
要点:不要在没有重新抛出它们的情况下处理任务体内的异常.
以下是优雅处理任务取消的方法:
var cts = new CancellationTokenSource( 5000 ); // auto-cancel in 5 sec.
Task.Run( () => {
cts.Token.ThrowIfCancellationRequested();
// do background work
cts.Token.ThrowIfCancellationRequested();
// more work
}, cts.Token ).ContinueWith( task => {
if ( !task.IsCanceled && task.IsFaulted ) // suppress cancel exception
Logger.Log( task.Exception ); // log others
} );
Run Code Online (Sandbox Code Playgroud)
var cts = new CancellationTokenSource( 5000 ); // auto-cancel in 5 sec.
var taskToCancel = Task.Delay( 10000, cts.Token );
// do work
try { await taskToCancel; } // await cancellation
catch ( OperationCanceledException ) {} // suppress cancel exception, re-throw others
Run Code Online (Sandbox Code Playgroud)
你可以这样做:
public void DoWork(CancellationToken cancelToken)
{
try
{
//do work
cancelToken.ThrowIfCancellationRequested();
//more work
}
catch (OperationCanceledException) when (cancelToken.IsCancellationRequested)
{
throw;
}
catch (Exception ex)
{
Log.Exception(ex);
throw;
}
}
Run Code Online (Sandbox Code Playgroud)
小智 7
C#6.0有一个解决方案.. 过滤异常
int denom;
try
{
denom = 0;
int x = 5 / denom;
}
// Catch /0 on all days but Saturday
catch (DivideByZeroException xx) when (DateTime.Now.DayOfWeek != DayOfWeek.Saturday)
{
Console.WriteLine(xx);
}
Run Code Online (Sandbox Code Playgroud)