简体   繁体   English

为什么不能在主线程中直接捕获和处理工作线程抛出的异常?

[英]Why is it not possible to directly catch and handle an exception thrown from a worker thread in the Main thread?

I am reading multithreading in Albahari's c# in a nutshell.简而言之,我正在阅读 Albahari 的 c# 中的多线程。 He says that if a thread (for example Main thread) creates and starts a worker thread, then an exception thrown by the worker thread cannot directly be caught and handled by the creating thread.他说,如果一个线程(例如主线程)创建并启动了一个工作线程,那么工作线程抛出的异常不能直接被创建线程捕获和处理。 Let me quote him verbatim:让我逐字引用他:

"Any try / catch / finally blocks in effect when a thread is created are of no relevance to the thread when it starts executing. Consider the following program: “创建线程时生效的任何 try / catch / finally 块在线程开始执行时与线程无关。考虑以下程序:

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

        catch (Exception ex)
        {  
           // We'll never get here!
           Console.WriteLine ("Exception!");
        }
    }


    static void Go() 
    { 
       
        throw null;     // Throws a NullReferenceException

    } 

Albahari goes on to say that: "The try / catch statement in this example is ineffective, and the newly created thread will be encumbered with an unhandled NullReferenceException. This behavior makes sense when you consider that each thread has an independent execution path." Albahari 继续说:“这个例子中的 try/catch 语句是无效的,新创建的线程将被未处理的 NullReferenceException 所阻碍。当你考虑到每个线程都有独立的执行路径时,这种行为是有意义的。”

So here is the crux of my question:所以这是我问题的症结所在:

I don't get the relevance of "each thread has an independent execution path".我不明白“每个线程都有独立的执行路径”的相关性。 I mean why should it matter if the execution paths are independent?我的意思是,如果执行路径是独立的,为什么重要? I mean when the workerThread throws an unhandled exception -- why can't the CLR just halt the Main thread and hand over the exception object to the catch block in the Main?我的意思是当workerThread抛出一个未处理的异常时——为什么CLR不能停止主线程并将异常object交给主线程中的catch块? What's stopping it??是什么阻止了它??

[NOTES: [笔记:

  1. The other related question How do I handle exceptions from worker threads and main thread in a single catch block?另一个相关问题如何在单个 catch 块中处理来自工作线程和主线程的异常? is NOT asking the same question --- and none of the elaborate answers express WHY can't the CLR marshal an unhandled exception from a worker thread to the Main thread没有问同样的问题 --- 没有一个详尽的答案表达了为什么 CLR 不能将未处理的异常从工作线程编组到主线程
  2. Similar question is asked about another language, C++, -- where the answers suggest that it has something to do with the two threads having different stacks and the logical impossibility of mixing the two while unwinding the stack during exception handling.类似的问题被问到另一种语言 C++,答案表明它与具有不同堆栈的两个线程有关,并且在异常处理期间展开堆栈时混合两者在逻辑上是不可能的。 I'm not sure whether those answers apply here to a managed execution environment, like that of c#.我不确定这些答案是否适用于托管执行环境,例如 c#。
    ] ]

The main thread might have already finished executing.主线程可能已经完成执行。

I know it is hard to understand at first, but the worker thread is not executed like a method call even if it looks like that.我知道一开始很难理解,但是即使看起来像方法调用,工作线程也不会像方法调用那样执行。 The thread is created inside that try-block, but the execution might happen much later or not at all.该线程是在该 try 块内创建的,但执行可能会更晚发生或根本不会发生。 Source code in text form can not make this visible.文本形式的源代码无法使其可见。 There seems to be some kind of "spacial" proximity of the try-block and the thread, but the moment the thread is created, the spacial proximity is gone. try-block 和线程之间似乎存在某种“空间”接近,但是在创建线程的那一刻,空间接近就消失了。 The try block only handles any exceptions that happen while the thread is created, but the it is "detached" from it. try 块仅处理创建线程时发生的任何异常,但它与它“分离”。

Analogy: The main thread is the manager (team lead) and the worker thread(s) are the workers (team members) reporting to that manager.类比:主线程是经理(团队领导),工作线程是向该经理报告的工作人员(团队成员)。 The workers are working in their home office.工人们正在他们的家庭办公室工作。 In the morning the manager is sending them emails with tasks for the day ("execute method Go").早上,经理向他们发送包含当天任务的电子邮件(“执行方法 Go”)。 Since the manager can not see the workers doing their work, she can only notice any progress or lack of it, if the workers send progress reports from time to time.由于经理看不到工人在做他们的工作,她只能在工人不时发送进度报告的情况下注意到任何进展或不足。 If workers fall off their chairs or down the stairs (exceptions), the manager would not know.如果工人从椅子上摔下来或从楼梯上摔下来(例外),经理不会知道。 The workers need to make sure to catch such exceptions and send an appropriate message to the manager to let her know.工作人员需要确保捕获此类异常并向经理发送适当的消息让她知道。 The manager is (in this case) not waiting around after sending the initial emails, but is doing other work in the meantime.经理(在这种情况下)在发送初始电子邮件后不会等待,而是在此期间做其他工作。

You are asking why the CLR can't halt the Main thread, and handle the exception of the child thread.您在问为什么 CLR 不能停止主线程,并处理子线程的异常。 Do you mean the main thread should always suspend execution automatically, immediately after launching a new Thread ?您的意思是主线程应该总是在启动新Thread后立即自动暂停执行吗? This would be very unhelpful, since a program could then have at a maximum one and only one active thread.这将非常没有帮助,因为一个程序最多只能有一个且只有一个活动线程。 It would be the end of multithreading as we know it.正如我们所知,这将是多线程的终结。 So you probably mean that the CLR should offer a mechanism to do this suspension of execution on demand, Although such a mechanism is indeed not available: you can do something very close to it using tasks:因此,您可能的意思是 CLR 应该提供一种机制来按需暂停执行,尽管这种机制确实不可用:您可以使用任务做一些非常接近它的事情:

public static void Main()
{
    try
    {
        Task workerTask = new Task(() =>
        {
            throw null;
        }, TaskCreationOptions.LongRunning); // Use a dedicated thread
        workerTask.Start();
        workerTask.Wait();
    }
    catch (Exception ex)
    {
        Console.WriteLine("Exception!"); // It happens!
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM