2010-01-09 40 views
3

我想爲我的個人項目開發一些異步方法,並且正在研究框架以供參考。將對象字段分配給局部變量有什麼優勢?

我已經下載了.NET source code來看一看位和螺栓更緊密地(與開發商的意見,一些反射並沒有給我們:-P)

不管怎樣,在很多.NET的班我遇到了以下模式:

class SomeType 
{ 
    // ... 
    SomeClass m_Field; 
    // ... 
    SomeClass SomeMethod() 
    { 
    SomeClass localField = m_Field; 
    if (localField == null) 
    { 
     localField = new SomeClass(); 
     m_Field = localField; 
    } 
    return localField; 
    } 
} 

這讓我想知道使用這種模式的優點是什麼?

據我所知,上面的圖案更糟糕的是,在性能方面,除了下面的一個:

class SomeType 
{ 
    // ... 
    SomeClass m_Field; 
    // ... 
    SomeClass SomeMethod() 
    { 
    if (m_Field == null) 
    { 
     m_Field = new SomeClass(); 
    } 
    return m_Field; 
    } 
} 

還是我失去了一些東西?

+1

你能舉一個具體的例子嗎?你看過SomeClass的什麼類型?它是某種類型的集合嗎? – 2010-01-09 23:12:49

回答

2

在許多情況下,所不同的是純粹的審美和主觀的,但三個原因考慮一個與其他浮現在腦海中:

  1. 線程安全:無鎖算法可能需要擔心這個,但是如果所有的同步都是通過鎖來完成的話,那應該不是問題。

  2. 性能:可能稍微快一點在某些情況下,但老實說,我懷疑它會在大多數情況下的差異。

  3. 異常安全性:通常您需要小心把中間變爲當地人,並只有在操作已經沒有引發異常完成結果發佈到各個領域。這充當事務處理機制,因爲沒有人會看到只有一半字段設置的對象。

+1

堆棧溢出只是挑戰我,我可能不是人。我是新來的,今天很開心。 – 2010-01-09 23:30:26

+0

難道你不喜歡他們展示的機器人西裝男生的照片! – 2010-01-09 23:40:32

0

這可能只是暗示編譯器應該將字段讀入寄存器而不是在內存中反覆訪問它。沒有什麼理由說明爲什麼第一個版本應該是更差性能明顯比你列出的第二個版本。它幾乎是編譯器生成的相同代碼。將對象字段讀入寄存器,測試它是否爲空,根據需要對其進行修改,然後將其寫回內存中的對象字段。

2

當您從一個線程調用SomeMethod時,此類方法可能有助於保護您,但在您檢查m_Field爲null後,控制權將傳遞給另一個將其設置爲null的線程。然後控制返回到第一個線程,但它仍然認爲m_Field!= null,這可能會導致NullReferenceException

據我記得在Richter的「CLR via C#」中有幾個關於它的字眼事件。

0

MS .NET JIT確實register allocation。對於這樣一個簡單的情況,該臨時變量最終應該保存在寄存器中,而不是堆棧中。生成的x86字節代碼的運行速度應該比類成員讀取兩次(一次檢查爲空,一次返回)非空的情況更快,並且空的情況也更快。

代碼生成器必須儘快,因爲它們發生在對象一般寫入修改字段,從對象字段每次被引用時讀取,否則在某些情況下,一個線程可能會從未看變化由另一個線程創建的對象,反之亦然。

但如果使用本地變量編譯器假定它沒有寫入到本地變量的變化(甚至把它存儲在堆棧),因爲訪問另一個線程的當地人是不是東西,C#允許。

相關問題