2015-05-27 86 views
5

我正在向庫中添加依賴注入,我使用Unity。 我想知道是否需要採取一些額外步驟使Unity Container線程安全。我發現了幾篇討論線程安全容器的文章(例如:http://www.fascinatedwithsoftware.com/blog/post/2012/01/04/A-Thread-Safe-Global-Unity-Container.aspx),但我不明白在我的項目中是否真的需要它。從另一方面來說,我不想因爲另一方面的競爭條件而產生一些令人討厭的錯誤,我不知道在什麼情況下會出現競爭狀況。我想用團結與成分根模式和靜態構造函數一樣,註冊的所有種類:什麼是使UnityContainer不是線程安全的缺陷?

internal static class ConfiguredUnityContainer 
    { 
     private static readonly UnityContainer Container = new UnityContainer(); 

     static ConfiguredUnityContainer() 
     { 
      Container.RegisterType<IConnectionFactory<SqlConnection>>(); 
     } 

     public static T Resolve<T>() 
     { 
      return Container.Resolve<T>(); 
     } 
    } 

所以,基本上我的問題是:在什麼情況下,我需要更多的線程安全的,當我一起工作Unity DI?我在哪裏可以獲得線程安全的競爭條件或問題?

回答

8

如果沒有註冊並行解析,Unity(和所有常用容器)由其設計者保證是線程安全的(或至少是sort of)。換句話說,只要您將註冊階段從解決階段和僅從容器中解析出的一個階段分開,您可以從多個線程並行調用Resolve,而不會出現任何問題。

事實上,作爲一種最佳實踐,您應該始終嚴格區分註冊階段和解決階段,因爲這會導致嚴重的麻煩並且很難找到競爭條件。

這些階段的這種分離是非常重要的,有些DI庫(如AutofacSimple Injector)在你強迫這種模式(這裏簡單的注射器是最嚴格的兩個)。簡單注射器文檔包含一個very clear explanation,說明爲什麼簡單注射器強制您使用該模型,並解釋如果您能夠更改配置會發生什麼情況。在這裏引用的那部分解釋(但你一定要閱讀所有的解釋):與線程安全

問題當用戶在Web請求期間改變 登記容易出現。如果容器在請求期間允許註冊更改 ,則其他請求可能會直接受到這些更改的影響(因爲通常每個AppDomain應該只有一個Container實例,即 )。取決於諸如 註冊的生活方式;工廠的使用以及圖表的結構如何構成,可能會有另一個 請求同時獲得舊註冊和新註冊。以 爲例,用一個不同的替換的臨時註冊。如果 這樣做,而不同的線程的對象圖正在被解析,而服務被注入到圖的多個點中 - 該圖將包含該抽象的不同實例 ,同一時間具有不同的生命期要求 - 和 這是不好的。

在我看來,那the article要鏈接去多進Service Locator anti-pattern和應用依賴注入的區別正確,這意味着只有訪問您的Composition Root容器內部。那篇文章的作者(Larry Spencer)並不十分清楚,但是在他的Composition Root中,他創建了一個單一的Unity容器並在整個應用程序中使用它。從某種意義上說,它仍然是'全局'的,但他阻止通過應用程序訪問該實例(因爲這是服務定位器模式)。

雖然編寫者試圖圍繞Unity容器創建一個線程安全的包裝器,但他的嘗試是天真的。他所做的是在每個RegisterResolve方法周圍創建一個鎖。這不僅會在多線程應用程序中造成巨大的擁塞,而且也無法解決在已經構建和緩存對象圖之後註冊和替換實例時會發生什麼問題,因爲簡單注入器文檔解釋了這一點,並且Unity question顯示。

+2

這個答案很棒,這就是我一直在尋找的!謝謝! – vmg

3

在依賴注入的情況下,線程安全注意事項比容器級別更深層次。您註冊到容器中的依賴類型也具有重要意義。在大多數情況下,您在Container中註冊的依賴項是單例。如果你的容器是靜態的,那麼它對所有線程都是全局的。換句話說,每個線程都可以訪問相同的單例實例。因此,如果您正在註冊的依賴項維護一個狀態(有狀態),那麼您需要考慮其他線程可能會更改該狀態。爲了避免這種頭痛:

1)你可以限制自己註冊無狀態的依賴關係。

2)您可以創建一個[ThreadStatic]統一實例。在這種情況下,每個線程都有它自己的統一實例,並且有狀態的依賴關係不會成爲問題。

3)最好的選擇是使用Unity的PerThreadLifetimeManager進行有狀態的依賴關係。這將保證每個線程都有自己的依賴實例。

+1

我通常不會建議每個線程有一個容器,因爲這會使事情變得非常複雜,但我完全同意使註冊組件成爲無狀態單例。這讓你的生活變得更簡單。 +1 – Steven

相關問題