C# 包含具有infinte循环的线程的对象的垃圾回收

C# 包含具有infinte循环的线程的对象的垃圾回收,c#,garbage-collection,C#,Garbage Collection,让我们假设我有一个类正在做类似的事情: public class Foo { private bool _forceStop = false; private Queue<object> queue; private void ProcessInBackground() { while(!forceStop ) { Moniter.Enter(queue);

让我们假设我有一个类正在做类似的事情:

 public class Foo
 {
     private bool _forceStop = false;
     private Queue<object> queue;
     private void ProcessInBackground()
     {
          while(!forceStop )
          {
              Moniter.Enter(queue);
              while(!_forceStop && queue.Count == 0)Moniter.Wait(queue);
              object data = null;                 
              if (!_forceStop)
                 data = queue.Dequeue(); 
              Moniter.Exit(queue);

              if (data != null)
                 processData(data);
           }
      }

      ...
  }
公共类Foo
{
private bool_forceStop=假;
专用队列;
私有void ProcessInBackground()
{
而(!forceStop)
{
进入(队列);
while(!\u forceStop&&queue.Count==0)monitor.Wait(queue);
对象数据=null;
如果(!\u强制停止)
data=queue.Dequeue();
监视器退出(队列);
如果(数据!=null)
过程数据(数据);
}
}
...
}
如果Foo类的对象不再被使用,并且该对象中的_forceStop从未设置为true,并且假设调用了ProcessInBackground,那么会收集它吗


编辑:解决了歧义,增加了线程安全性。很抱歉,当我编写示例时,我刚刚编写了一个场景。

是的,将收集
对象数据
(假设
processData()
未将其放入列表中)。 GC可以处理(托管)线程,如果它不能处理,世界将结束

但是您的代码不是线程安全的,在您放弃锁后,您将退出队列

关于改进的建议:

//untested
private bool _forceStop = false;
private object _locker = new object();  // better not depend on queue here

private void ProcessInBackground()
{
    while(true)
    {
       // Moniter.Enter(queue);
       lock(_locker)
       {       
          while(!_forceStop && queue.Count == 0)
            Moniter.Wait(_locker);

          //Moniter.Exit(queue);
          if (_forceStop) break;

          object data = queue.Dequeue(); 
       }
       processData(data);
    }
}
编辑:

在第二次阅读时,这是关于包含对象的。当然,线程不会收集这些信息

假设“对象”是指拥有该方法的实例,那么如果某个线程正在执行ProcessInBackground方法,则该实例的根是该线程(它是arg0,并且在循环中使用它的字段)。所以不,它不会被收集

如果你所说的“对象”是指“数据”,那么除了最近的以外,所有的都是符合条件的。最新版本可能取决于编译器配置(是否消除了本地?)和CLI(是否检查未读取的本地?)。我会说“它可能符合条件,处于发布/优化状态;可能不处于调试/未优化状态”。

(像Marc一样,我假设您指的是调用
ProcessInBackground
的对象。我还假设
队列
是一个字段。)

否-您仍在引用
队列
,这意味着将读取对象中的字段,这意味着无法对包含的对象进行垃圾收集

请注意,仅仅因为方法在对象中运行并不会阻止垃圾回收-垃圾回收器只关心引用本身是否被使用,或者对象中的字段是否被读取。演示代码显示:

using System;
using System.Threading;

public class OddFinalizer   
{
    int field;

    public OddFinalizer(int field)
    {
        this.field = field;
    }

    ~OddFinalizer()
    {
        Console.WriteLine("OddFinalizer");
    }

    public void Work()
    {
        for (int i = 0; i < 5; i++)
        {
            Console.WriteLine("In loop before last access...");
            GC.Collect();
            GC.WaitForPendingFinalizers();            
            Thread.Sleep(1000);
        }
        Console.WriteLine("Field value: {0}", field);
        for (int i = 0; i < 5; i++)
        {
            Console.WriteLine("In loop after last access...");
            GC.Collect();
            GC.WaitForPendingFinalizers();
            Thread.Sleep(1000);
        }
    }

    static void Main(string[] args)
    {
        new OddFinalizer(10).Work();
    }
}
而且_forceStop从未设置为true,对象是否会被收集

没有

当对象的一个方法位于线程的调用堆栈上时,不能对其进行垃圾收集。调用堆栈具有对对象的引用

在本例中,
ProcessInBackground
是线程调用堆栈上的一个方法


Jon的回答纠正了我的回答-垃圾收集器在确信不再使用引用时工作,包括
引用。这意味着可以在对象在调用堆栈上具有方法时收集对象(此方法可能使用其他方法,但不使用此实例自己的任何成员)

在我自己的代码中,我没有终结器。当我收集到一些物品时,我很少关心它们。当我希望收集这些数据时,它们此时不在调用堆栈上。如果它们是在调用堆栈上收集的,因为.net认为这是一件好事,我没问题


我不认为这些细节会改变我应该如何编写任何代码,所以我将选择继续编写代码,就好像我上面的错误事实是真实的一样,同时稍微注意它的错误性。如果我的情况与你的情况相似,你也可以这样做。

这不取决于排队的位置吗?@vlad henk是对的-你应该持锁排队或退队here@Marc我不是指线程安全。我指的是这样一个事实,如果其他对象引用了同一队列,那么停止线程并收集它将改变程序的语义。除非发生一些奇怪的优化,否则在appdomain关闭之前不应该收集该对象。线程仍然引用包含对象的字段。@Vlad线程安全性是相关的。如果他在循环中没有内存障碍,编译器可以决定
\u forceStop
queue.Count
在循环中永远不会改变,从而优化中断条件。但我认为你有内隐记忆障碍,这是他安全的地方。我认为是关于
对象数据=…
@Henk,我在编辑中添加了这两个。另请参阅我关于调试的说明-有什么想法吗?请看我的答案,以获取一个实例方法的示例,其中在实例方法运行时调用终结器-我假设这已经足够证明GC的资格:)@HenkHolterman:鉴于这个短语,我以与Marc相同的方式阅读这个问题“如果对象包含该方法”@marcGravel:Righto,更好:)@DavidB-4种阅读问题的方法。编程本身非常精确。@David几乎不清楚OP没有指定它们是指哪个对象……你应该真正定义“对象”和“该方法”“这样你就能清楚地知道你在说什么。虽然这对您来说似乎很明显,但对回答问题的专家来说显然并不明显。假设不涉及任何I/O,并且所有读写操作都限制在同一对象内的字段中,他们还会阻止对象被收集吗?@Vlad:是的-GC不关心字段的值会发生什么变化-只是它们正在被读取。啊-唉,我想是的。我不想留下虚假信息。将不得不考虑编辑,以纳入这一新事实。
In loop before last access..
In loop before last access..
In loop before last access..
In loop before last access..
In loop before last access..
Field value: 10
In loop after last access...
OddFinalizer
In loop after last access...
In loop after last access...
In loop after last access...
In loop after last access...