टास्क में अपवाद को पकड़ने का सबसे अच्छा तरीका क्या है?


83

इसके साथ 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
             */

        });        
    }
}

जवाबों:


109

ऐसा करने के दो तरीके हैं, जिस भाषा का आप उपयोग कर रहे हैं उसके संस्करण पर निर्भर है।

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

गणना का 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();

1
अनाम विधि में आप अपवाद प्रकार को कैसे जानेंगे? अगर मैं t.Exception, intellisense अभ्यस्त बेनकाब, संदेश ... आदि गुण ...
guiomie

4
@guiomie t है अपवाद।
कैस्परऑन

2
संदर्भ परिभाषित नहीं है कि यह क्या है?
मॉन्स्टरमोरपीजी

@MonsterMMORPG SynchronizationContext, यदि आवश्यक हो।
कैस्परऑन

उत्तर के लिए Ty हमें इसकी आवश्यकता क्यों होगी? मेरा मतलब है किस मामले में? क्या यह पर्याप्त है ? myTask.ContinueWith (t => ErrorLogger.LogError ("func_CheckWaitingToProcessPages में हुई त्रुटि कार्य और त्रुटि शुरू हुई:" + t), TaskContinuationOO.OnlyOnFaulted);
मॉन्स्टरमोरपीजी

5

आप कुछ कस्टम टास्क फैक्ट्री बना सकते हैं, जो एंबेडेड हैंडलिंग प्रोसेसिंग के साथ टास्क का उत्पादन करेगी। कुछ इस तरह:

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

आप अपने ग्राहक कोड में इस कारखाने से उत्पादित कार्य के लिए अपवाद प्रसंस्करण के बारे में भूल सकते हैं। उसी समय में आप अभी भी इस तरह के टास्क को खत्म करने का इंतजार कर सकते हैं या फायर-एंड-फॉरगेट स्टाइल में उनका उपयोग कर सकते हैं:

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

लेकिन अगर मैं ईमानदार हूं तो मुझे यकीन नहीं है कि आप कोडिंग को पूरा करना चाहते हैं। किसी भी मामले में यह निर्णय आपके आवेदन के तर्क पर निर्भर करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.