【问题标题】:What happens to a thread when the original class goes out of scope当原始类超出范围时线程会发生什么
【发布时间】:2015-05-18 20:29:47
【问题描述】:

为了清楚起见,我简化了下面的示例,但我在现场制作程序中遇到了这个问题,我看不出它是如何工作的!

public class Test
{
    static void Main() 
    {
        Counter foo = new Counter();
        ThreadStart job = new ThreadStart(foo.Count);
        Thread thread = new Thread(job);
        thread.Start();
        Console.WriteLine("Main terminated");
    }
}

public class Counter
{
    public void Count()
    {
        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine("Other thread: {0}", i);
            Thread.Sleep(500);
        }
        Console.WriteLine("Counter terminated");
    }
}

主例程启动计数器线程,主例程终止。计数器线程继续进行,不管是否给出以下输出。

Main terminated    
Other thread: 0
Other thread: 1
Other thread: 2
Other thread: 3
Other thread: 4
Other thread: 5   
Other thread: 6
Other thread: 7    
Other thread: 8    
Other thread: 9
Counter terminated

我的示例程序演示了虽然调用类不再存在,但线程仍然存在直到完成。但是,我的理解是,一旦一个类超出范围,它的资源最终会被垃圾回收清理掉。

在我的现实生活场景中,该线程会发送持续 1-2 小时的大量电子邮件。我的问题是“垃圾收集最终会杀死线程还是 GC 会知道线程仍在处理”?我的电子邮件线程会一直运行到完成,还是存在异常终止的危险?

【问题讨论】:

  • 不过,我的理解是,一旦一个类超出范围,它的资源最终会被垃圾回收清理掉。线程是一个特例……它们携带Thread 对象“在”自身“内部”,在Thread.CurrentThread 中,它们当前的运行方法被认为是 GC Root(因此 GC 发现对象是否仍然具有参考)...但是,是的,这是一个循环推理。
  • 类不会超出范围。对象不会超出范围。变量超出范围(这很容易理解 - 这意味着变量不再存在。如果变量是引用,它不会对它引用的对象做任何事情)。

标签: c# .net multithreading


【解决方案1】:

来自System.Threading.Thread

一旦启动线程,就不必保留对 Thread 对象的引用。线程继续执行,直到线程过程完成。

所以即使Thread 对象未被引用,线程仍然会运行。

【讨论】:

    【解决方案2】:

    查看System.Threading.Thread.IsBackground 的文档

    如果线程不是后台线程,它将阻止应用程序关闭,直到它完成。

    【讨论】:

    • isBackground 解释了它。当我将其设置为 true 时,程序会按我的预期立即终止。感谢您为我指出正确的地方。
    • @MortimerCat 这与垃圾收集无关。这是一个完全独立的行为。线程的入口点正在运行的对象没有被收集,而是因为没有活动的线程而整个进程被拆除。这与作用域、GC 或任何对象无关;重要的是是否有任何前台线程在运行。
    • 是的,@Servy 为您关于垃圾收集的问题提供了更好的解释;这个答案更多的是关于“我该如何改变这种意外行为?”
    • 我的问题确实询问了垃圾收集,但在我的脑海中,我试图理解为什么线程从未终止(我现在知道这是因为它作为前台线程运行)。 isBackground 是我的灯泡时刻。
    【解决方案3】:

    不过,我的理解是,一旦一个类超出范围,它的资源最终会被垃圾回收清理掉。

    这可以更准确地说:

    一旦一个对象实例不再可以通过托管引用从任何可执行代码访问,它就符合垃圾回收的条件。

    当您创建一个正在执行特定对象的方法的新线程时,您可以在该线程的整个生命周期内访问该对象的内容。只有当 GC 能够证明任何应用程序的线程不再可能再次访问该对象时,GC 才能清理它。由于您的代码可以仍然访问对象实例,因此它不会被 GC。

    【讨论】:

      【解决方案4】:

      变量的作用域是让编译器判断该变量是否可以被其他方法访问。线程是一个运行对象,由运行时控制。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-12-03
        • 1970-01-01
        • 1970-01-01
        • 2015-10-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多