Görev'de istisna yakalamanın en iyi yolu nedir?


83

İle System.Threading.Tasks.Task<TResult>atılabilecek istisnaları yönetmem gerekiyor. Bunu yapmanın en iyi yolunu arıyorum. Şimdiye kadar, çağrı içindeki tüm yakalanmamış istisnaları yöneten bir temel sınıf oluşturdum..ContinueWith(...)

Bunu yapmanın daha iyi bir yolu olup olmadığını merak ediyorum. Ya da bunu yapmanın iyi bir yolu olsa bile.

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
             */

        });        
    }
}

Yanıtlar:


109

Kullandığınız dilin sürümüne bağlı olarak bunu yapmanın iki yolu vardır.

C # 5.0 ve üstü

Bunu sizin için büyük ölçüde basitleştirmek için asyncve awaitanahtar kelimelerini kullanabilirsiniz .

asyncve Görev Paralel Kitaplığı'nıawait kullanmayı basitleştirmek için dile tanıtıldı, bu da kullanmak zorunda kalmanızı önledi ve yukarıdan aşağı bir şekilde programlamaya devam etmenize olanak tanıdı.ContinueWith

Bu nedenle , istisnayı yakalamak için basitçe a try/catch block kullanabilirsiniz, örneğin:

try
{
    // Start the task.
    var task = Task.Factory.StartNew<StateObject>(() => { /* action */ });

    // Await the task.
    await task;
}
catch (Exception e)
{
    // Perform cleanup here.
}

Yukarıdaki enkapsüle yöntem Not zorunluluk kullanımı var asynckullanabilirsiniz böylece anahtar kelime uygulanan await.

C # 4.0 ve altı

Numaralandırmadan bir değer alan ContinueWithaşırı yüklemeyi kullanarak istisnaları işleyebilirsiniz , örneğin:TaskContinuationOptions

// Get the task.
var task = Task.Factory.StartNew<StateObject>(() => { /* action */ });

// For error handling.
task.ContinueWith(t => { /* error handling */ }, context,
    TaskContinuationOptions.OnlyOnFaulted);

OnlyOnFaultedÜyesi TaskContinuationOptionsnumaralandırma devamı gerektiğini gösterir sadece öncül görev bir istisna attı durumlarda icra edilmesi.

Elbette, ContinueWithistisnai olmayan durumu ele alarak, aynı öncülden birden fazla çağrı yapabilirsiniz :

// 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();

1
Anonim yöntemdeki istisna türünü nasıl bilebilirsiniz? T.Exception yaparsam, intellisense innerexception, message ... etc özellikleri açığa çıkarmaz ...
guiomie

4
@guiomie t olan istisna.
casperOne

2
bağlam tanımlanmadı nedir?
MonsterMMORPG

@MonsterMMORPG SynchronizationContext, gerekirse.
casperOne

Cevap için Ty neden buna ihtiyacımız olsun? Yani hangi durumda? bu yeterli mi ? myTask.ContinueWith (t => ErrorLogger.LogError ("func_CheckWaitingToProcessPages görev başlatıldığında hata oluştu ve hata:" + t), TaskContinuationOptions.OnlyOnFaulted);
MonsterMMORPG

5

İstisna işleme işlemi gömülü olarak Görevler üretecek bazı özel Görev fabrikası oluşturabilirsiniz. Bunun gibi bir şey:

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

İstemci kodunuzda bu fabrikada üretilen Görevler için istisnaların işlenmesini unutabilirsiniz. Aynı zamanda, bu tür Görevlerin tamamlanmasını bekleyebilir veya bunları Ateş Et ve Unut tarzında kullanabilirsiniz:

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

Ama dürüst olmak gerekirse, neden tamamlama koduna sahip olmak istediğinizden emin değilim. Her durumda bu karar, uygulamanızın mantığına bağlıdır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.