捕获在不同线程中抛出的异常

发布于 2024-11-06 20:48:04 字数 205 浏览 0 评论 0 原文

我的一个方法 (Method1) 生成一个新线程。 该线程执行一个方法 (Method2),并在执行期间引发异常。 我需要获取有关调用方法的异常信息 (Method1)

是否可以通过某种方式捕获 Method1 中在 Method2 中引发的异常?

One of my method (Method1) spawns a new thread.
That thread execute a method (Method2) and during exectution an exception is thrown.
I need to get that exception information on the calling method (Method1)

Is there someway I can catch this exception in Method1 that is thrown in Method2?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

请别遗忘我 2024-11-13 20:48:04

.NET 4及更高版本中,您可以使用Task类而不是创建新线程。然后,您可以使用任务对象上的 .Exceptions 属性来获取异常。
有两种方法可以做到这一点:

  1. 在单独的方法中: // 在某些任务的线程中处理异常

    类程序
    {
        静态无效主(字符串[]参数)
        {
            任务任务=新任务(测试);
            task.ContinueWith(ExceptionHandler, TaskContinuationOptions.OnlyOnFaulted);
            任务.Start();
            Console.ReadLine();
        }
    
        静态 int 测试()
        {
            抛出新的异常();
        }
    
        静态无效异常处理程序(任务任务)
        {
            var 异常 = 任务.Exception;
            Console.WriteLine(异常);
        }
    }
    
  2. 在同一方法中: // 在调用者线程中处理异常强>线程

    类程序
    {
        静态无效主(字符串[]参数)
        {
            任务任务=新任务(测试);
            任务.Start();
    
            尝试
            {
                任务.Wait();
            }
            catch(聚合异常前)
            {
                Console.WriteLine(ex);    
            }
    
            Console.ReadLine();
        }
    
        静态 int 测试()
        {
            抛出新的异常();
        }
    }
    

请注意,您得到的异常是 AggregateException。所有真正的异常都可以通过 ex.InnerExceptions 属性获得。

.NET 3.5中,您可以使用以下代码:

  1. //您在子线程中处理异常

    类程序
    {
        静态无效主(字符串[]参数)
        {
            异常异常=空;
            Thread 线程 = new Thread(() => SafeExecute(() => Test(0, 0), Handler));
            线程.Start();            
    
            Console.ReadLine();
        }
    
        private static void Handler(Exception异常)
        {        
            Console.WriteLine(异常);
        }
    
        private static void SafeExecute(操作测试,操作<异常>处理程序)
        {
            尝试
            {
                测试.Invoke();
            }
            catch(异常前)
            {
                处理程序(前);
            }
        }
    
        静态无效测试(int a,int b)
        {
            抛出新的异常();
        }
    }
    
  2. 或者 // 您在调用者线程中处理异常

    类程序
    {
        静态无效主(字符串[]参数)
        {
            异常异常=空;
            Thread thread = new Thread(() => SafeExecute(() => Test(0, 0), out 异常));
    
            线程.Start();            
    
            线程.Join();
    
            Console.WriteLine(异常);    
    
            Console.ReadLine();
        }
    
        private static void SafeExecute(Action 测试,out Exception 异常)
        {
            异常=空;
    
            尝试
            {
                测试.Invoke();
            }
            catch(异常前)
            {
                例外=前;
            }
        }
    
        静态无效测试(int a,int b)
        {
            抛出新的异常();
        }
    }
    

In .NET 4 and above, you can use Task<T> class instead of creating new thread. Then you can get exceptions using .Exceptions property on your task object.
There are 2 ways to do it:

  1. In a separate method: // You process exception in some task's 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 the same method: // You process exception in the caller's 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();
        }
    }
    

Note that the exception which you get is AggregateException. All real exceptions are availible through ex.InnerExceptions property.

In .NET 3.5 you can use the following code:

  1. // You process exception in the child's thread

    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. Or // You process exception in the caller's 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();
        }
    }
    
°如果伤别离去 2024-11-13 20:48:04

您无法捕获 Method1 中的异常。但是,您可以捕获 Method2 中的异常并将其记录到原始执行线程可以读取和使用的变量中。

You can not catch the exception in Method1. You can, however, catch the exception in Method2 and record it to a variable that the original thread of execution can then read and work with.

千柳 2024-11-13 20:48:04

我有一个特殊的问题,因为我想使用集成测试套件中包含控件的项目,因此必须创建一个 STA 线程。我最终得到的代码如下,放在这里以防其他人遇到同样的问题。

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

这是直接粘贴代码。对于其他用途,我建议提供一个操作或函数作为参数,并在线程上调用它,而不是对被调用的方法进行硬编码。

I had a particular problem in that I wanted to use items, containing controls, from an integration test suite, so have to create an STA thread. The code I ended up with is as follows, put here in case others have the same issue.

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

This is a direct paste of the code as-is. For other uses I would recommend supplying an action or function as a parameter and invoking that on the thread instead of hard-coding the called method.

场罚期间 2024-11-13 20:48:04

在不同线程之间共享数据的最简单方法是共享数据,如下所示(有些是伪代码):

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

您可以在 关于多线程的精彩介绍,但是,我更喜欢在 O'Reilly 的书《C# 3.0》中阅读有关此内容的内容简而言之,作者是 Albahari 兄弟(2007 年),它也可以在 Google 图书上免费访问,就像这本书的新版本一样,因为它还涵盖了线程池、前台线程与后台线程等内容,并且具有良好的和简单的示例代码。 (免责声明:我拥有这本书的一本破旧副本)

如果您正在制作 WinForms 应用程序,则使用共享数据特别方便,因为 WinForm 控件不是线程安全的。使用回调将数据从工作线程传递回 WinForm 控件,主 UI 线程需要带有 Invoke() 的丑陋代码才能使该控件线程安全。使用共享数据和单线程 System.Windows.Forms.Timer(例如 0.2 秒的短 Interval),您可以轻松地从工作线程发送信息到没有 Invoke 的控件。

The simplest method to share data between different threads is shared data as follows (some is 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;
   }
}

You can read about this method in this nice introduction about multithreading, however, I preferred to read about this in the O'Reilly book C# 3.0 in a nutshell, by the brothers Albahari (2007), which is also freely accessible on Google Books, just like the newer version of the book, because it also covers thread pooling, foreground versus background threads, etc etc, with nice and simple example code. (Disclaimer: I own a worn-out copy of this book)

In case you are making a WinForms application, the use of shared data is especially handy, because the WinForm controls are not thread-safe. Using a callback to pass data from the worker thread back to a WinForm control the main UI thread needs ugly code with Invoke() to make that control thread-safe. Using shared data instead, and the single-threaded System.Windows.Forms.Timer, with a short Interval of say 0.2 seconds, you can easily send information from the worker thread to the control without Invoke.

一抹淡然 2024-11-13 20:48:04

这是我用来将异常抛出回主线程以进行捕获的代码。

class Program
{
    static void Main(string[] args)
    {
        CancellationTokenSource cancelToken = new CancellationTokenSource();
        Exception taskException = null;

        var timerTask = Task.Factory.StartNew(() =>
        {
            for (;;)
            {
                if (cancelToken.IsCancellationRequested)
                    break;

                ContinuousTask();

                Thread.Sleep(400);

            }
        }, cancelToken.Token).ContinueWith((t, o) => {
            taskException = t.Exception;
            ((Thread)o).Interrupt();
        }, Thread.CurrentThread, TaskContinuationOptions.OnlyOnFaulted);

        try
        {
            
            //do a bunch of tasks here

            //want to skip the do while and go to the catch if exception is thrown
            do
            {
                System.Threading.Thread.Sleep(200);
            } while (true);

        }
        catch
        {
            if (taskException != null)
                Console.WriteLine(taskException.Message);
        }
        
    }

    private static int _loopCounter = 0;
    public static void ContinuousTask()
    {
        int counter = 0;

        do
        {

            if (_loopCounter >= 3)
                throw new Exception("error");

            if (counter >= 5)
                break;

            counter += 1;
            System.Threading.Thread.Sleep(100);

        } while (true);

        _loopCounter += 1;
    }

}

Here is the code that I used to throw the exception back to the main thread to be caught.

class Program
{
    static void Main(string[] args)
    {
        CancellationTokenSource cancelToken = new CancellationTokenSource();
        Exception taskException = null;

        var timerTask = Task.Factory.StartNew(() =>
        {
            for (;;)
            {
                if (cancelToken.IsCancellationRequested)
                    break;

                ContinuousTask();

                Thread.Sleep(400);

            }
        }, cancelToken.Token).ContinueWith((t, o) => {
            taskException = t.Exception;
            ((Thread)o).Interrupt();
        }, Thread.CurrentThread, TaskContinuationOptions.OnlyOnFaulted);

        try
        {
            
            //do a bunch of tasks here

            //want to skip the do while and go to the catch if exception is thrown
            do
            {
                System.Threading.Thread.Sleep(200);
            } while (true);

        }
        catch
        {
            if (taskException != null)
                Console.WriteLine(taskException.Message);
        }
        
    }

    private static int _loopCounter = 0;
    public static void ContinuousTask()
    {
        int counter = 0;

        do
        {

            if (_loopCounter >= 3)
                throw new Exception("error");

            if (counter >= 5)
                break;

            counter += 1;
            System.Threading.Thread.Sleep(100);

        } while (true);

        _loopCounter += 1;
    }

}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文