Каков наилучший способ поймать исключение в задаче?
С System.Threading.Tasks.Task<TResult>
, Я должен управлять исключениями, которые могут быть брошены. Я ищу лучший способ сделать это. До сих пор я создал базовый класс, который управляет всеми неперехваченными исключениями внутри вызова .ContinueWith(...)
мне интересно, есть ли лучший способ сделать это. Или даже если это хороший способ сделать это.
public class BaseClass
{
protected void ExecuteIfTaskIsNotFaulted<T>(Task<T> e, Action action)
{
if (!e.IsFaulted) { action(); }
else
{
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
{
/* I display a window explaining the error in the GUI
* and I log the error.
*/
this.Handle.Error(e.Exception);
}));
}
}
}
public class ChildClass : BaseClass
{
public void DoItInAThread()
{
var context = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew<StateObject>(() => this.Action())
.ContinueWith(e => this.ContinuedAction(e), context);
}
private void ContinuedAction(Task<StateObject> e)
{
this.ExecuteIfTaskIsNotFaulted(e, () =>
{
/* The action to execute
* I do stuff with e.Result
*/
});
}
}
2 ответа:
есть два способа сделать это, в зависимости от версии языка, который вы используете.
в C# 5.0 и выше
вы можете использовать
async
иawait
ключевые слова, чтобы упростить многое из этого для вас.
async
иawait
были введены в язык, чтобы упростить использование Параллельных Задач Библиотека, предотвращая вас от использованияContinueWith
и что вы продолжать программировать в нисходящем порядке.из-за этого, вы можете просто использовать
try
/catch
блок, чтобы поймать исключение, вот так:try { // Start the task. var task = Task.Factory.StartNew<StateObject>(() => { /* action */ }); // Await the task. await task; } catch (Exception e) { // Perform cleanup here. }
обратите внимание, что метод инкапсуляции выше должны использовать есть
async
ключевое слово применяется, так что вы можете использоватьawait
.в C# 4.0 и ниже
вы можете обрабатывать исключения с помощью
ContinueWith
перегрузка это принимает значение отTaskContinuationOptions
перечисление, например:// Get the task. var task = Task.Factory.StartNew<StateObject>(() => { /* action */ }); // For error handling. task.ContinueWith(t => { /* error handling */ }, context, TaskContinuationOptions.OnlyOnFaulted);
The
OnlyOnFaulted
членTaskContinuationOptions
перечисление указывает, что продолжение должно только выполняется, если предшествующая задача вызвала исключение.конечно, вы можете иметь более одного вызова
ContinueWith
от того же антецедента, обрабатывая не исключительный случай:// Get the task. var task = new Task<StateObject>(() => { /* action */ }); // For error handling. task.ContinueWith(t => { /* error handling */ }, context, TaskContinuationOptions.OnlyOnFaulted); // If it succeeded. task.ContinueWith(t => { /* on success */ }, context, TaskContinuationOptions.OnlyOnRanToCompletion); // Run task. task.Start();
вы можете создать некоторую пользовательскую фабрику задач, которая будет производить задачи с встроенной обработкой обработки исключений. Что-то вроде этого:
using System; using System.Threading.Tasks; class FaFTaskFactory { public static Task StartNew(Action action) { return Task.Factory.StartNew(action).ContinueWith( c => { AggregateException exception = c.Exception; // Your Exception Handling Code }, TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously ).ContinueWith( c => { // Your task accomplishing Code }, TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously ); } public static Task StartNew(Action action, Action<Task> exception_handler, Action<Task> completion_handler) { return Task.Factory.StartNew(action).ContinueWith( exception_handler, TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously ).ContinueWith( completion_handler, TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.ExecuteSynchronously ); } };
вы можете забыть об обработке исключений для задач, созданных с этой фабрики в вашем клиентском коде. В то же время вы все еще можете дождаться завершения таких задач или использовать их в стиле Fire-and-Forget:
var task1 = FaFTaskFactory.StartNew( () => { throw new NullReferenceException(); } ); var task2 = FaFTaskFactory.StartNew( () => { throw new NullReferenceException(); }, c => { Console.WriteLine("Exception!"); }, c => { Console.WriteLine("Success!" ); } ); task1.Wait(); // You can omit this task2.Wait(); // You can omit this
но если честно, я не совсем уверен, почему вы хотите иметь код обработки завершения. В любом случае это решение зависит от логики вашего приложения.