web-dev-qa-db-fra.com

attraper l'exception qui est jetée dans un fil différent

Une de mes méthodes (Method1) génère un nouveau thread. Ce thread exécute une méthode (Method2) et lors de l'exécution, une exception est levée . Je dois obtenir les informations sur cette exception sur la méthode d'appel (Method1)

Est-il possible que je puisse intercepter cette exception dans Method1 qui est levée dans Method2?

103

Dans .NET 4 et les versions ultérieures, vous pouvez utiliser la classe Task<T> au lieu de créer un nouveau thread. Ensuite, vous pouvez obtenir des exceptions en utilisant la propriété .Exceptions sur votre objet de tâche . Il existe deux façons de le faire:

  1. Dans une méthode distincte: // Vous traitez une exception dans un thread de la tâche _

    class Program
    {
        static void Main(string[] args)
        {
            Task<int> task = new Task<int>(Test);
            task.ContinueWith(ExceptionHandler, TaskContinuationOptions.OnlyOnFaulted);
            task.Start();
            Console.ReadLine();
        }
    
        static int Test()
        {
            throw new Exception();
        }
    
        static void ExceptionHandler(Task<int> task)
        {
            var exception = task.Exception;
            Console.WriteLine(exception);
        }
    }
    
  2. Dans la même méthode: // Vous traitez une exception dans le thread appelant

    class Program
    {
        static void Main(string[] args)
        {
            Task<int> task = new Task<int>(Test);
            task.Start();
    
            try
            {
                task.Wait();
            }
            catch (AggregateException ex)
            {
                Console.WriteLine(ex);    
            }
    
            Console.ReadLine();
        }
    
        static int Test()
        {
            throw new Exception();
        }
    }
    

Notez que l'exception que vous obtenez est AggregateException. Toutes les exceptions réelles sont disponibles via la propriété ex.InnerExceptions.

Dans .NET 3.5, vous pouvez utiliser le code suivant:

  1. // Vous traitez l'exception dans le thread child

    class Program
    {
        static void Main(string[] args)
        {
            Exception exception = null;
            Thread thread = new Thread(() => SafeExecute(() => Test(0, 0), Handler));
            thread.Start();            
    
            Console.ReadLine();
        }
    
        private static void Handler(Exception exception)
        {        
            Console.WriteLine(exception);
        }
    
        private static void SafeExecute(Action test, Action<Exception> handler)
        {
            try
            {
                test.Invoke();
            }
            catch (Exception ex)
            {
                Handler(ex);
            }
        }
    
        static void Test(int a, int b)
        {
            throw new Exception();
        }
    }
    
  2. Ou // vous traitez une exception dans le thread caller

    class Program
    {
        static void Main(string[] args)
        {
            Exception exception = null;
            Thread thread = new Thread(() => SafeExecute(() => Test(0, 0), out exception));
    
            thread.Start();            
    
            thread.Join();
    
            Console.WriteLine(exception);    
    
            Console.ReadLine();
        }
    
        private static void SafeExecute(Action test, out Exception exception)
        {
            exception = null;
    
            try
            {
                test.Invoke();
            }
            catch (Exception ex)
            {
                exception = ex;
            }
        }
    
        static void Test(int a, int b)
        {
            throw new Exception();
        }
    }
    
170
oxilumin

Vous ne pouvez pas intercepter l'exception dans Method1. Vous pouvez toutefois intercepter l'exception dans Method2 et l'enregistrer dans une variable que le thread d'exécution d'origine peut ensuite lire et utiliser.

9
ermau

La méthode la plus simple pour partager des données entre différents threads est shared data comme suit (certains sont du pseudo-code):

class MyThread
{
   public string SharedData;

   public void Worker()
   {
      ...lengthy action, infinite loop, etc...
      SharedData = "whatever";
      ...lengthy action...
      return;
   }
}

class Program
{
   static void Main()
   {
      MyThread m = new MyThread();
      Thread WorkerThread = new Thread(m.Worker);
      WorkerThread.Start();

      loop//or e.g. a Timer thread
      {
         f(m.SharedData);
      }
      return;
   }
}

Vous pouvez lire sur cette méthode dans cette belle introduction sur le multithreading , cependant, j’ai préféré en parler dans le O'Reilly book C# 3.0 in a nutshell, des frères Albahari (2007), qui est également accessible librement sur Google Books version du livre, car il couvre également le regroupement de threads, les threads d’avant-plan par rapport aux threads d’arrière-plan, etc., avec un exemple de code simple et agréable. (Avertissement: je possède un exemplaire usé de ce livre)

Si vous créez une application WinForms, l'utilisation de données partagées est particulièrement pratique car les contrôles WinForm ne sont pas thread-safe. Utilisation d'un rappel pour transmettre les données du thread de travail à un contrôle WinForm, le thread d'interface utilisateur principal a besoin d'un code laid avec Invoke() pour rendre ce contrôle thread-safe. En utilisant des données partagées à la place, et le System.Windows.Forms.Timer à thread unique, avec une Interval courte de 0,2 seconde par exemple, vous pouvez facilement envoyer des informations du thread de travail au contrôle sans Invoke.

1
Roland

J'ai eu un problème particulier en ce que je voulais utiliser des éléments, contenant des contrôles, d'une suite de tests d'intégration, donc je devais créer un thread STA. Le code que j'ai obtenu est le suivant, mis ici au cas où d'autres auraient le même problème.

    public Boolean? Dance(String name) {

        // Already on an STA thread, so just go for it
        if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) return DanceSTA(name);

        // Local variable to hold the caught exception until the caller can rethrow
        Exception lException = null;

        Boolean? lResult = null;

        // A gate to hold the calling thread until the called thread is done
        var lGate = new ManualResetEvent(false);

        var lThreadStart = new ThreadStart(() => {
            try {
                lResult = DanceSTA(name);
            } catch (Exception ex) {
                lException = ex;
            }
            lGate.Set();
        });

        var lThread = new Thread(lThreadStart);
        lThread.SetApartmentState(ApartmentState.STA);
        lThread.Start();

        lGate.WaitOne();

        if (lException != null) throw lException;

        return lResult;
    }

    public Boolean? DanceSTA(String name) { ... }

Ceci est une pâte directe du code tel quel. Pour d'autres utilisations, je recommanderais de fournir une action ou une fonction en tant que paramètre et de l'invoquer sur le thread au lieu de coder en dur la méthode appelée.

0
Richard Petheram