gpt4 book ai didi

c# - 如何阻止对象(以及其中的所有内容)被终结?

转载 作者:太空宇宙 更新时间:2023-11-03 16:08:52 24 4
gpt4 key购买 nike

在我为期两周的解决问题的探索中:

根本问题是垃圾收集器。我的对象的 Finalizer 本来是在对象即将被释放(即回收其资源)时使用react的理想时间。 .NET 垃圾收集系统的问题在于,当 my 终结器被调用时,我拥有的其他对象很可能已经被终结。

如果我与 native 类对象互操作,这个问题就很容易解决。垃圾收集器无法释放我背后的那些对象(并且未经我的许可)。因此,当我的托管对象的终结器被调用时,我知道我的内部状态仍然有效。

我需要的是一种告诉垃圾收集器放开你的手的方法。

有没有办法阻止对象被终结?

例如

例如,下面的代码是错误的,因为终结器错误地将私有(private)的东西 _values对象仍然存在。实际上,它可能已经在我手下完成:

class Sqm
{
private List<Value> _values = new List<Value>();

//finalizer
public ~Sqm()
{
Shutdown();
}

protected void Shutdown()
{
foreach (var value in _values) //<-- crash; _values no longer exists
SaveValueToHardDrive(value); //<-- crash; value no longer exists
}
}

我需要的是告诉垃圾收集器不要完成该列表对象或其中的任何对象的方法:

class Sqm
{
private List<Value> _values = new List<Value>();

//constructor
public Sqm()
{
GC.LetMeManuallyFinalize(_values);
}

//finalizer
public ~Sqm()
{
Shutdown();
GC.ManuallyFinalize(_values);
}

protected void Shutdown()
{
foreach (var value in _values)
SaveValueToHardDrive(value);
}
}

这有两个可能的问题:

  • 没有 GC.ManuallyFinalize方法
  • 它可能会抑制释放 _values列出自己,但它引用的对象可能仍会在我的背后完成:

      protected void Shutdown()
    {
    foreach (var value in _values)
    SaveValueToHardDrive(value); //<---crash, contained object already finalized
    }

所以现在我需要确保添加到列表中的对象也从最终确定中排除:

public void AddSample(String name, Int64 value)
{
Entry entry = GetEntryByName(name);
if (entry == null)
{
entry = new Entry();
GC.LetMeManuallyFinalize(entry);
}

entry.Count += 1;
entry.Sum += value;
entry.Average = entry.Sum / entry.Count;
}

//finalizer
public ~Sqm()
{
foreach (var value in _values)
GC.ManuallyFinalize(value);

GC.ManuallyFinalize(_values);
}

这可能有一个问题,即 Entry没有其他内部对象,我不知道 List<T> .垃圾收集器可能会在 _values 上执行不需要的剖腹手术。尽管 _values本身还没有最终确定。

使用 GCAlloc.Alloc()

@MatthewWatson 有个好主意。我认为指出错误的原因会很有用。使用 GCAlloc.Alloc持有对象的引用。然后您可以在终结器期间使用它来访问它:

public Sqm()
{
private List<Value> _values = new List<Value>();
private GCHandle _valuesHandle; //handle to keep _values alive

//constructor
Sqm()
{
//prevent _values from being finalized
_valuesHandle = GCAlloc.Alloc(_values);
}

//finalizer
~Sqm()
{
try
{
Shutdown(_values); //Safe, right? RIGHT? _values couldn't have been finalized
}
finally
{
_valuesHandle.Free();
}
}

private void Shutdown(List<Values> values)
{
foreach (var value in values)
{
//The list itself might not have been finalized
//But objects used internally to Microsoft's List<T> class have been finalized
//and objects in the list itself were already finalized
SaveValueToHardDrive(value); //<--BAD: values inside list were already finalized
}
}
}

注意:它也可能因为伪记录的行为而失败。来自 The Truth About GCHandles :

When you create a new GCHandle, a new entry in the AppDomain's handle table is created. This entry is kept until the handle is freed (via GCHandle.Free()) or the AppDomain is unloaded.

强调我的。

原来如此。我需要告诉垃圾收集器

Do not finalize this object (and everything inside it)

棘手的部分是我不拥有的类的内部私有(private)成员;甚至一个对象仍然被 GCAlloc 引用仍然会有它依赖的对象在它背后完成。

示例用法

public static Foo
{
public static Sqm = new Sqm();
}

Foo.Sqm.AddSample("QueryCustomerInfo", stopwatch.TotalMicroseconds);

最佳答案

无论如何,在终结期间进行任何长期操作(例如输入输出)都是非常糟糕的做法。您应该考虑将对象的 _values 列表存储在静态列表中——这将防止值被破坏。当您的对象正在完成时,您可以将对内部列表的引用保存在另一个定期检查的静态列表中。当它包含对列表的引用时,这意味着您的对象已被破坏并且应该保存它包含的值。

class Sqm
{
private static List<List<Value>> = _lists = new List<List<Value>>();
private static List<List<Value>> = _finalizationQueue = new List<List<Value>>();
private List<Value> _values = new List<Value>();
Sqm() { _lists.Add(_values); }
~Sqm() { _finalizationQueue.Add(_values); }
public static void CheckAndSave()
{
foreach(var list in _finalizationQueue)
SaveValues(list);
}
}

更新:如果域可能在您不希望它关闭时关闭,您唯一的方法是将值存储在另一个域中。

关于c# - 如何阻止对象(以及其中的所有内容)被终结?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18255218/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com