Moh*_*ara 35 c# concurrency multithreading exception-handling task
我从Task.Run中捕获异常时遇到了问题.我更改了代码并解决了问题.我愿意弄清楚在这两种方式下处理Task.Run中的异常有什么区别:
在Outside函数中我无法捕获异常,但在Inside中我可以捕获它.
void Outside()
{
try
{
Task.Run(() =>
{
int z = 0;
int x = 1 / z;
});
}
catch (Exception exception)
{
MessageBox.Show("Outside : " + exception.Message);
}
}
void Inside()
{
Task.Run(() =>
{
try
{
int z = 0;
int x = 1 / z;
}
catch (Exception exception)
{
MessageBox.Show("Inside : "+exception.Message);
}
});
}
Run Code Online (Sandbox Code Playgroud)
Mat*_*son 36
运行任务时,如果某些内容等待任务的结果或任务完成,则会保留并重新抛出它抛出的任何异常.
Task.Run()返回一个Task可用于执行此操作的对象,因此:
var task = Task.Run(...)
try
{
task.Wait(); // Rethrows any exception(s).
...
Run Code Online (Sandbox Code Playgroud)
对于较新版本的C#,您可以使用await而不是Task.Wait():
try
{
await Task.Run(...);
...
Run Code Online (Sandbox Code Playgroud)
这更整洁.
为了完整起见,这是一个可编译的控制台应用程序,演示如下使用await:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp1
{
class Program
{
static void Main()
{
test().Wait();
}
static async Task test()
{
try
{
await Task.Run(() => throwsExceptionAfterOneSecond());
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
static void throwsExceptionAfterOneSecond()
{
Thread.Sleep(1000); // Sleep is for illustration only.
throw new InvalidOperationException("Ooops");
}
}
}
Run Code Online (Sandbox Code Playgroud)
小智 35
使用Task.Wait的想法将完成这一操作,但会导致调用线程(如代码所示)等待并因此阻塞,直到任务完成,这有效地使代码同步而不是异步.
而是使用Task.ContinueWith选项来实现结果:
Task.Run(() =>
{
//do some work
}).ContinueWith((t) =>
{
if (t.IsFaulted) throw t.Exception;
if (t.IsCompleted) //optionally do some work);
});
Run Code Online (Sandbox Code Playgroud)
如果任务需要在UI线程上继续,请将TaskScheduler.FromCurrentSynchronizationContext()选项作为参数继续使用,如下所示:
).ContinueWith((t) =>
{
if (t.IsFaulted) throw t.Exception;
if (t.IsCompleted) //optionally do some work);
}, TaskScheduler.FromCurrentSynchronizationContext());
Run Code Online (Sandbox Code Playgroud)
此代码将简单地从任务级别重新抛出聚合异常.当然,您也可以在此处介绍其他形式的异常处理.
在您的外部代码中,您只检查启动任务是否不会引发异常而不是任务主体本身。它异步运行,然后启动它的代码完成。
您可以使用:
void Outside()
{
try
{
Task.Run(() =>
{
int z = 0;
int x = 1 / z;
}).GetAwaiter().GetResult();
}
catch (Exception exception)
{
MessageBox.Show("Outside : " + exception.Message);
}
}
Run Code Online (Sandbox Code Playgroud)
使用.GetAwaiter().GetResult()等待直到任务结束并按原样传递抛出的异常并且不将它们包装在AggregateException.
您可以等待,然后异常冒泡到当前同步上下文(参见 Matthew Watson 的回答)。或者,正如 Menno Jongerius 提到的,您可以ContinueWith保持代码异步。请注意,只有在使用OnlyOnFaultedcontinuation 选项引发异常时才能这样做:
Task.Run(()=> {
//.... some work....
})
// We could wait now, so we any exceptions are thrown, but that
// would make the code synchronous. Instead, we continue only if
// the task fails.
.ContinueWith(t => {
// This is always true since we ContinueWith OnlyOnFaulted,
// But we add the condition anyway so resharper doesn't bark.
if (t.Exception != null) throw t.Exception;
}, default
, TaskContinuationOptions.OnlyOnFaulted
, TaskScheduler.FromCurrentSynchronizationContext());
Run Code Online (Sandbox Code Playgroud)