webentwicklung-frage-antwort-db.com.de

catch-Ausnahme, die in einem anderen Thread ausgelöst wird

Eine meiner Methoden (Method1) erzeugt einen neuen Thread. Dieser Thread führt eine Methode (Method2) aus und während der Ausführung wird eine Ausnahme ausgelöst. Ich benötige diese Ausnahmeinformationen für die aufrufende Methode (Method1).

Kann ich diese Ausnahme in Method1 abfangen, das in Method2 geworfen wird?

103

In .NET 4 und darüber können Sie die Klasse Task<T> verwenden, anstatt einen neuen Thread zu erstellen. Dann können Sie Ausnahmen mit der .Exceptions-Eigenschaft für Ihr Task-Objekt erhalten ..__ Es gibt zwei Möglichkeiten, dies zu tun:

  1. In einer separaten Methode: // Sie verarbeiten eine Ausnahme in einem task - Thread

    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. In derselben Methode: // Sie verarbeiten eine Ausnahme im Aufrufer - Thread

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

Beachten Sie, dass die Ausnahme, die Sie erhalten, AggregateException ist. Alle realen Ausnahmen sind über die ex.InnerExceptions-Eigenschaft verfügbar.

In .NET 3.5 können Sie den folgenden Code verwenden:

  1. // Sie verarbeiten eine Ausnahme im 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. Oder // Sie bearbeiten eine Ausnahme im Aufrufer - Thread

    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

Sie können die Ausnahme in Method1 nicht abfangen. Sie können jedoch die Ausnahme in Methode2 abfangen und in einer Variablen aufzeichnen, die der ursprüngliche Ausführungsthread dann lesen und bearbeiten kann.

9
ermau

Die einfachste Methode, Daten zwischen verschiedenen Threads gemeinsam zu nutzen, ist shared data wie folgt (einige sind 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;
   }
}

Sie können diese Methode in dieser Nizza-Einführung zum Multithreading nachlesen. Ich habe es jedoch vorgezogen, im O'Reilly book C# 3.0 in a nutshell von den Brüdern Albahari (2007) zu lesen, der ebenso wie der neuere auf Google Books frei zugänglich ist Version des Buches, da sie auch Thread-Pooling, Vordergrund- und Hintergrund-Threads usw. mit Nice und einfachem Beispielcode abdeckt. (Haftungsausschluss: Ich besitze eine abgenutzte Kopie dieses Buches.)

Wenn Sie eine WinForms-Anwendung erstellen, ist die Verwendung gemeinsam genutzter Daten besonders praktisch, da die WinForm-Steuerelemente nicht threadsicher sind. Bei Verwendung eines Rückrufs, um Daten vom Worker-Thread zurück an ein WinForm-Steuerelement zu übergeben, benötigt der Haupt-UI-Thread hässlichen Code mit Invoke(), um das Steuerelement threadsicher zu machen. Verwenden Sie stattdessen gemeinsam genutzte Daten und den Singlethread-Code System.Windows.Forms.Timer mit einer kurzen Interval von beispielsweise 0,2 Sekunden. Sie können problemlos Informationen aus dem Worker-Thread ohne Invoke an das Steuerelement senden.

1
Roland

Ich hatte ein besonderes Problem, da ich Elemente verwenden wollte, die Steuerelemente aus einer Integrationstest-Suite enthielten, also einen STA-Thread erstellen müssen. Der Code, mit dem ich am Ende gelandet bin, lautet wie folgt, wenn andere das gleiche Problem haben.

    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) { ... }

Dies ist eine direkte Einfügung des Codes in der vorliegenden Form. Für andere Zwecke würde ich empfehlen, eine Aktion oder Funktion als Parameter anzugeben und diese im Thread aufzurufen, anstatt die aufgerufene Methode fest zu codieren.

0