当原始类超出范围时,线程会发生什么?

18

为了更加清晰易懂,我简化了以下示例。但我在一个实际生产程序中遇到这个问题,无法看出它是如何工作的!

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.CurrentThread中携带Thread对象,并且它们当前运行的方法被认为是GC根(因此是GC发现对象是否仍有引用的起点)... 但是,是的,这是循环推理。 - xanatos
3
类不会超出范围。对象也不会超出范围。变量会超出范围(这很容易理解——它意味着变量不存在了。如果变量是引用类型,则不会对其所指向的对象产生影响)。 - user253751
4个回答

16

来自 System.Threading.Thread

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

因此,即使未引用 Thread 对象,线程仍将运行。


10

isBackground解释了这个问题。当我将其设置为true时,程序立即终止,正如我所预期的那样。感谢您指引我到正确的地方。 - MortimerCat
1
@MortimerCat 这与垃圾回收无关。这是一种完全不同的行为。线程入口点正在运行的对象没有被收集,而是整个进程被关闭,因为没有活动线程。这与作用域、GC或任何对象都无关;唯一重要的是是否有任何前台线程在运行。 - Servy
是的,@Servy 对于你关于垃圾回收的问题提供了更好的解释;而这个答案则更多地关注于“我该如何改变这种意外的行为?” - adv12
我的问题确实涉及垃圾回收,但我当时在思考的是为什么线程从未终止(现在我知道是因为它作为前台线程运行)。isBackground 属性让我恍然大悟。 - MortimerCat

10
然而,我理解的是,一旦一个类超出范围,它的资源将最终被垃圾回收清理。

这可以更精确地说明:

一旦一个对象实例不再通过托管引用从任何可执行代码访问,它就变得有资格进行垃圾回收。
当您创建一个正在执行特定对象方法的新线程时,您使该对象内容在该线程的整个生命周期内都可以访问。只有当GC能够证明应用程序的任何线程都无法再次访问该对象时,它才能对其进行清理。由于您的代码仍然可以访问对象实例,因此它不会被GC清除。

1

变量的作用域是由编译器确定变量是否可以被其他方法访问。线程是由运行时控制的正在运行的对象。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接