ParameterizedThreadStart是否保证对象实例不会被垃圾回收?

时间:2012-04-24 09:00:22

标签: c# delegates lambda thread-safety

我一直在玩下面这段代码:

class RunMeBaby
{
    public void Start()
    {
        while (true)
        {
            Console.WriteLine("I'm " + Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(1000);
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        RunMeBaby r = new RunMeBaby();
        Thread t = new Thread(r.Start);  // ParameterizedThreadStart delegate
        r = null;
        GC.Collect(GC.MaxGeneration);
        t.Start();

        r = new RunMeBaby();
        t = new Thread(() => r.Start()); // ThreadStart delegate
        t.Start();
        //Thread.Sleep(1000);
        r = null;
    }
}

虽然main的第一部分是顺利执行的,但当我对Thread.Sleep()方法的调用进行注释时,第二部分失败了,我得到了一个null异常。

我的理解是lambda表达式被懒惰地评估,可能会发生新线程没有足够快地启动而主要线程首先将r设置为null。现在我将这个“第二部分”放在静态方法中,r具有局部范围,问题就消失了。但是我想知道在特定情况下线程调度程序是否隐藏了该问题,可能在具有不同工作负载的不同机器上它仍然可能发生。或者有一些关于lambda表达式的东西可以保证即使r超出范围,只要它没有被设置为null,它仍然以某种方式被引用。

最后我想知道我是否应该考虑尽可能多地使用ParameterizedThreadStart代表,或者坚持使用lambdas,因为我尊重某些条件以保证它们有效。

2 个答案:

答案 0 :(得分:5)

在我们讨论垃圾收集之前,首先让我们理解您编写的代码。

之间存在巨大差异:

new Thread(r.Start)

Start的当前值上创建r方法的委托,即

new Thread(new ThreadStart(r.Start)) // identical to new Thread(r.Start)

在上述任何一个中,r被评估为现在,因此稍后对另一个实例(或null)的更改不会影响它。对比:

new Thread(() => r.Start())

这是一个匿名方法,捕获变量r,即r仅在调用匿名方法时评估,即第二个线程正在运行。因此,是的:如果您更改r的值,您可能会得到不同的结果(如果您将其更改为null,则会出错)。

参数化线程启动也可以工作:

new Thread(state => ((RunMeBaby)state).Start(), r);

r当前值作为参数值传递,因此现在已修复;当调用委托时,state获取当时r (之前)的值,因此您可以将其转换为适当的类型并安全地使用它。

现在!在垃圾收集方面,没有什么特别要知道的。是的,将引用r传递给ParameterizedThreadStart将创建引用的副本(不是对象的副本),因此将防止垃圾收集,直到它不再存在在适用范围。然而,原始的new Thread(r.Start)方法也是如此。唯一一次变得棘手的是“捕获的变量”示例(() => r.Start()),虽然您看到的问题没有任何与GC有关,而且与规则有关捕获变量。

答案 1 :(得分:2)

很多我不明白的问题。我能说的是

Thread t = new Thread(r.Start)

创建一个委托,该委托将在内部指向提供通过委托调用的方法实现的实例。因此,将r设置为null将不起作用,因为存在 Thread - >的关系。 ParameterizedDelegate - >你的方法 - > RunMeBaby

r = new RunMeBaby();
t = new Thread(() => r.Start());

在r周围创建一个闭包,它仍然允许你修改r。如果您将其设置为null并稍后访问它,则会获得 NRE