2012-11-19 116 views
0

注意下面的代碼來自asp.net。閉合對象上的垃圾收集


如果我有以下

AmazonS3 s3Client = Amazon.AWSClientFactory.CreateAmazonS3Client(); 

// ... 
// details elided 
// ... 

BackgroundWorker worker = new BackgroundWorker(); 

worker.DoWork += new DoWorkEventHandler((s, args) => 
{ 
    s3Client.PutObject(titledRequest); 
}); 

new Thread(() => worker.RunWorkerAsync()).Start(); 

的(寫得不好的)代碼將垃圾收集足夠聰明,永遠,永遠收集s3Client對象,直到背景工人用它做?


注意,我只是爲了修復被asp.net的時候我直接斷火的背景工人出現這種情況中引發了一個惱人的錯誤拉開後臺工作線程的內部。

+0

沒錯。這是GC的用途。在線程失去所有引用之前,s3client將被保留! – quetzalcoatl

+0

你爲什麼要開始一個新線程來運行'RunWorkerAsync()'。這是一個開始的異步方法;該方法的內容只是創建一個新線程,配置它一下,然後啓動它。沒有必要在後臺線程中這樣做;這樣做會增加毫無意義的開銷。你也不需要定義使用lambdas時使用的委託,你可以完全移除'new DoWorkEventHandler';它將基於上下文隱含。 – Servy

+0

@Servy感謝關於不需要定義委託的提示。另一方面,當你啓動一個backgroundWorker時,asp.net會拋出令人難以置信的煩人錯誤。從周圍搜索似乎沒有一個簡單的修復,所以我用這個俗氣的解決方法。 –

回答

5

是的,它會的。編譯器將爲您生成一個新類,其中包含您在閉包中引用的每個當地人的字段。封閉體將被髮射到該類中的方法上,並且包含函數中的所有局部變量都將被編譯器重寫爲引用該閉包對象上的字段。

所有這些奇蹟發生在編譯時;運行時不需要知道任何關於它的信息。由於運行時已足夠智能,不會收集作爲委託對象的對象,因此閉包引用的局部對象的生存期將保證延伸至生成的委託對象的生存期。

爲了說明,編譯器會吐出來是這樣的:

[System.Runtime.CompilerServices.CompilerGeneratedAttribute] 
internal class ClosureImplementation // See note 1 
{ 
    public AmazonS3 s3Client; 

    public void Method(object s, EventArgs args) 
    { 
     s3Client.PutObject(titledRequest); // See note 2 
    } 
} 

然後,在你的方法,這是發出來代替:

ClosureImplementation closure = new ClosureImplementation(); 
closure.s3Client = Amazon.AWSClientFactory.CreateAmazonS3Client(); 

// ... 

worker.DoWork += closure.Method; 

注:

  1. 生成的類的名稱由編譯器選擇; ClosureImplementation只是一個例子。
  2. 我沒有足夠的上下文來知道titledRequest來自哪裏,所以我故意沒有說明編譯器如何處理它。
+0

哇 - 很好的答案,謝謝。我喜歡你的答案[這裏](http://stackoverflow.com/a/4147863/352552)btw :) –

3

總之,是的。

您正在爲您的事件處理程序使用lambda,並且正在關閉本地變量。這意味着你正在使用lambda定義塊之外的作用域中的變量。這意味着,當lambda被編譯器轉換爲「真實」方法時(在類中,具有真實名稱和一個對象實例和全部)它將包含對作爲該類的字段關閉的局部變量的引用。這將保持該對象的範圍,直到lambda不再被引用到任何地方或執行任何地方。正確。

+0

請注意,這也適用於匿名代表,而不僅僅是lambda表達式。 – cdhowie

+0

@cdhowie確實如此,但我認爲lambdas會使匿名委託過時。實際上他們不再需要他們了,我發現很少有人繼續使用他們。 – Servy

+0

Lambdas沒有任何語法,意思是「匹配預期的參數類型,不使用它們」。我仍然使用'委託{...}',因爲沒有lambda表達式的等價語法 - 你必須提供一個參數列表。另外,在可能的情況下,lambdas會更喜歡基於「基於表達式」的重載方法,否則將回退到基於委託的重載。在某些情況下,可能需要強制使用委託重載而不是「表達式」重載,因此在這種情況下也應使用匿名委託語法。 – cdhowie