Nice programing

Task에서 예외를 포착하는 가장 좋은 방법은 무엇입니까?

nicepro 2020. 11. 8. 11:02
반응형

Task에서 예외를 포착하는 가장 좋은 방법은 무엇입니까?


를 사용하면 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
             */

        });        
    }
}

사용중인 언어 버전에 따라 두 가지 방법으로이를 수행 할 수 있습니다.

C # 5.0 이상

당신은 사용할 수 있습니다 asyncawait당신이의 큰 거래를 단순화하는 키워드를.

asyncawait사용하여 단순화하는 언어에 도입 된 태스크 라이브러리를 병렬 사용하는 데에서 당신을 방지 ContinueWith하고 당신이 하향식 (top-down) 방식으로 프로그램을 계속 할 수 있도록.

이 때문에 다음과 같이 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);

열거 형 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();

You can create some custom Task factory, which will produce Tasks with exception handling processing embedded. Something like this:

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
        );
    }
};

You can forget about exceptions processing for Tasks produced from this factory in your client code. In the same time you still can wait finishing of such Tasks or use them in Fire-and-Forget style:

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

But if be honest I'm not really sure why you want to have completion handling code. In any case this decision depends on the logic of your application.

참고URL : https://stackoverflow.com/questions/12980712/what-is-the-best-way-to-catch-exception-in-task

반응형