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...