|
簡介:C#/ .NET應(yīng)該是垃圾回收. C#有一個析構(gòu)函數(shù),用于清理資源.當(dāng)一個對象A被垃圾收集在同一行我試圖克隆其變量成員之一時會發(fā)生什么?顯然,在多處理器上,有時,垃圾收集器贏了……
問題
今天,在關(guān)于C#的培訓(xùn)課程中,老師向我們展示了一些僅在多處理器上運行時才包含錯誤的代碼.
我將總結(jié)一下,有時候,編譯器或JIT通過在從被調(diào)用方法返回之前調(diào)用C#類對象的終結(jié)器來搞砸.
Visual C 2005文檔中給出的完整代碼將作為“答案”發(fā)布,以避免提出非常大的問題,但必要的內(nèi)容如下:
以下類具有“Hash”屬性,該屬性將返回內(nèi)部數(shù)組的克隆副本.在構(gòu)造中,數(shù)組的第一項值為2.在析構(gòu)函數(shù)中,其值設(shè)置為零.
關(guān)鍵是:如果你試圖得到“示例”的“哈?!睂傩?你將獲得一個干凈的數(shù)組副本,其第一個項目仍然是2,因為正在使用該對象(因此,不是垃圾收集/定稿):
public class Example
{
private int nValue;
public int N { get { return nValue; } }
// The Hash property is slower because it clones an array. When
// KeepAlive is not used, the finalizer sometimes runs before
// the Hash property value is read.
private byte[] hashValue;
public byte[] Hash { get { return (byte[])hashValue.Clone(); } }
public Example()
{
nValue = 2;
hashValue = new byte[20];
hashValue[0] = 2;
}
~Example()
{
nValue = 0;
if (hashValue != null)
{
Array.Clear(hashValue, 0, hashValue.Length);
}
}
}
但沒有什么是這么簡單…… 使用這個類的代碼是在一個線程內(nèi)部進(jìn)行的,當(dāng)然,對于測試,該應(yīng)用程序是多線程的:
public static void Main(string[] args)
{
Thread t = new Thread(new ThreadStart(ThreadProc));
t.Start();
t.Join();
}
private static void ThreadProc()
{
// running is a boolean which is always true until
// the user press ENTER
while (running) DoWork();
}
DoWork靜態(tài)方法是問題發(fā)生的代碼:
private static void DoWork()
{
Example ex = new Example();
byte[] res = ex.Hash; // [1]
// If the finalizer runs before the call to the Hash
// property completes, the hashValue array might be
// cleared before the property value is read. The
// following test detects that.
if (res[0] != 2)
{
// Oops... The finalizer of ex was launched before
// the Hash method/property completed
}
}
每隔1,000,000個DoWork提供一次,顯然,垃圾收集器會發(fā)揮其魔力,并試圖回收“ex”,因為它不再在函數(shù)的重新編寫代碼中引用,而這次,它比“哈?!备斓玫椒椒?所以我們最終得到的是一個零ed字節(jié)數(shù)組的克隆,而不是正確的一個(第一項為2).
我的猜測是代碼的內(nèi)聯(lián),它基本上取代了DoWork函數(shù)中標(biāo)記為[1]的行:
// Supposed inlined processing
byte[] res2 = ex.Hash2;
// note that after this line, "ex" could be garbage collected,
// but not res2
byte[] res = (byte[])res2.Clone();
如果我們認(rèn)為Hash2是一個簡單的訪問器,編碼如下:
// Hash2 code:
public byte[] Hash2 { get { return (byte[])hashValue; } }
所以,問題是:這應(yīng)該在C#/ .NET中以這種方式工作,還是可以認(rèn)為這是JIT編譯器的錯誤?
編輯
有關(guān)解釋,請參閱Chris Brumme和Chris Lyons的博客.
http://blogs./cbrumme/archive/2003/04/19/51365.aspx http://blogs./clyon/archive/2004/09/21/232445.aspx
每個人的回答都很有趣,但我不能選擇一個比另一個好.所以我給了你一個1 …
抱歉
??
編輯2
我無法在Linux / Ubuntu / Mono上重現(xiàn)問題,盡管在相同條件下使用相同的代碼(同時運行多個相同的可執(zhí)行文件,發(fā)布模式等) 解決方法: 它只是代碼中的一個錯誤:終結(jié)器不應(yīng)該訪問托管對象.
實現(xiàn)終結(jié)器的唯一原因是釋放非托管資源.在這種情況下,您應(yīng)該仔細(xì)實施the standard IDisposable pattern.
使用此模式,您可以實現(xiàn)受保護的方法“protected Dispose(bool disposing)”.從終結(jié)器調(diào)用此方法時,它會清除非托管資源,但不會嘗試清理托管資源.
在您的示例中,您沒有任何非托管資源,因此不應(yīng)該實現(xiàn)終結(jié)器. 來源:https://www./content-1-322801.html
|