【问题标题】:Exception handling in threads线程中的异常处理
【发布时间】:2009-10-12 12:04:01
【问题描述】:

最近我参加了一个采访。给了我一个代码 sn-p。我知道,面试官是从 albhari 的线程样本中获取的。

public static void Main() 
{
    try 
    {
        new Thread (Go).Start();
    }
    catch (Exception ex)
    {
        // We'll never get here!
       Console.WriteLine ("Exception!");
    }
}

static void Go() { throw null; }

将上述代码修改为

public static void Main()
{
    new Thread (Go).Start();
}

static void Go() 
{
    try 
    {
        ...
        throw null; // this exception will get caught below
        ...
    }
    catch (Exception ex) 
    {
        Typically log the exception, and/or signal another thread
        that we've come unstuck
        ...
    }
}

将是处理异常的好人选。

有人问我,“除了上述线索,还有哪些其他替代方案适合作为好的解决方案?很难找到替代方案,所以我在这里提出它以收集您的建议。

【问题讨论】:

    标签: c# multithreading


    【解决方案1】:

    在一个线程中抛出的异常通常无法在另一个线程中捕获。

    你最好在函数 Go 中捕获它并显式传递给主线程。

    但是,如果您只想记录来自所有线程的所有未处理消息,如果您正在开发 WinForms 或 WPF 应用程序,则可以在 Application 类中使用 AppDomain.UnhandledException 事件或等效事件。

    【讨论】:

    • ...但请注意,您无法处理 AppDomain.UnhandledException 中的异常,您会收到通知,但无论如何应用程序将被关闭。
    • 可以通过设置 v1.x 兼容模式来防止应用程序停止。为此,必须将 元素添加到 app.config 的
    【解决方案2】:

    还有哪些其他替代方案适合作为好的解决方案?

    解决方案是什么? 你想解决什么问题?

    如果您使用BackgroundWorker,而不是线程,它有一个RunWorkerCompleted 事件,您可以在其中检查错误属性的RunWorkerCompletedEventArgs 参数。这通常用于 WinForms 或 WPF 应用程序,因为 Visual Studio 设计器中对 BackgroundWorker 有很好的支持。

    您还可以为 Go() 定义一个委托,并在其上调用 BeginInvoke()。当然你也需要 EndInvoke() 。

    此外,启动随机线程通常不是一个好主意。 ThreadPool.QueueUserWorkItem、BackgroundWorker 或 asynch 委托都使用 ThreadPool,并且被推荐。

    【讨论】:

    • 我的意思是:原始代码被破坏了。您的第一个选择是另一种选择。如果它是令人满意的,那么你不需要其他选择。你想解决什么问题? “原始代码的替代方案”可以是anything。您可以提交解决数独难题的代码 - 这将是一种替代方案。会合适吗?没有要求,谁能说?
    • 不,先生,面试官问了这个问题。 :) 所以我什么都没说。而且我不知道如何解决它。 :) 感谢您的建议。
    • @Cheeso,请正确阅读问题,没有人需要任何建议,他在接受采访时遇到了聪明人告诉他寻找替代方案的情况,我相信请求者真的足够了解关于你所建议的一切。
    【解决方案3】:

    Joe Albahari 的网站上列出了替代方案: http://www.albahari.com/threading/#_Exception_Handling

    “但是,在某些情况下,您不需要处理工作线程上的异常,因为 .NET Framework 会为您完成。这些将在接下来的部分中介绍,并且是:
    -异步委托
    -BackgroundWorker
    -任务并行库(条件适用)"

    【讨论】:

      【解决方案4】:

      您可以使用AppDomain.UnhandledException 事件

      【讨论】:

        【解决方案5】:

        我认为这是最简单的方法是:

        BackgroundWorker bw = new BackgroundWorker();
        bw.DoWork += new DoWorkEventHandler((object sender2, DoWorkEventArgs e2) =>
        {
            throw new Exception("something bad");
            e2.Result = 1 + 1;
        });
        bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler((object sender2, RunWorkerCompletedEventArgs e2) =>
        {
            if (e2.Error != null)
            {
                Console.WriteLine("Error: " + e2.Error.Message);
            }
        });
        bw.RunWorkerAsync();
        

        但如果您想同步线程(也许这是在 GUI 线程以外的线程上),有些人可能更喜欢另一种方式:

            private class FileCopier
            {
                public bool failed = false;
                public Exception ex = null;
                public string localPath;
                public string dstPath;
                public FileCopier(string localPath, string dstPath)
                {
                    this.localPath = localPath;
                    this.dstPath = dstPath;
                }
        
                public void Copy()
                {
                    try{
                        throw new Exception("bad path");
                    }catch(Exception ex2)
                    {
                        ex = ex2;
                        failed = true;
                    }
                }
            }
        
            public static void Main()
            {
                FileCopier fc = new FileCopier("some path", "some path");
                Thread t = new Thread(fc.Copy);
                t.Start();
                t.Join();
                if (fc.failed)
                    Console.WriteLine(fc.ex.Message);
            }
        

        请注意,如果您有多个线程并循环遍历它们并加入所有线程,则第二个示例会更有意义...但我保持示例简单。

        第三种模式将使用更清洁的任务工厂:

        private static test(){
            List<Task<float>> tasks = new List<Task<float>>();
            for (float i = -3.0f; i <= 3.0f; i+=1.0f)
            {
                float num = i;
                Console.WriteLine("sent " + i);
                Task<float> task = Task.Factory.StartNew<float>(() => Div(5.0f, num));
                tasks.Add(task);
            }
        
            foreach(Task<float> t in tasks)
            {
                try
                {
                    t.Wait();
                    if (t.IsFaulted)
                    {
                        Console.WriteLine("Something went wrong: " + t.Exception.Message);
                    }
                    else
                    {
                        Console.WriteLine("result: " + t.Result);
                    }
                }catch(Exception ex)
                {
                    Console.WriteLine("Error: " + ex.Message);
                }
        
            }
        }
        
        private static float Div(float a, float b)
        {
            Console.WriteLine("got " + b);
            if (b == 0) throw new Exception("Divide by zero");
            return a / b;
        }
        

        【讨论】:

          猜你喜欢
          • 2013-09-10
          • 2013-10-06
          • 2014-03-23
          • 2011-04-07
          • 1970-01-01
          • 2014-09-10
          • 1970-01-01
          • 2011-02-07
          • 1970-01-01
          相关资源
          最近更新 更多