2014-06-20 112 views
4

我想實踐手動依賴注入(沒有框架),以消除我的代碼中的緊耦合。這只是爲了練習 - 我沒有具體的實現。並行處理的依賴注入

到目前爲止簡單的構造函數注入工作正常。

但是,我不能解決如何創建一個緊密耦合,當一個類必須使用並行循環內的另一個。例如:

public class Processor 
{ 
    private IWorker worker; 
    public Processor(IWorker worker) 
    { 
     this.worker = worker; 
    } 
    public List<string> DoStuff() 
    { 
     var list = new List<string>(); 
     for (int i = 0; i < 10; i++) 
     { 
      list.Add(worker.GetString()); 
     } 
     return list; 
    } 

    public List<string> DoStuffInParallel() 
    { 
     var list = new System.Collections.Concurrent.ConcurrentBag<string>(); 

     Parallel.For(0, 10, i => 
     { 
      //is there any trivial way to avoid this?? 
      list.Add(new Worker().GetString()); 
     }); 
     return list.ToList(); 
    } 
} 

public class Worker : IWorker 
{ 
    public string GetString() 
    { 
     //pretend this relies on some instance variable, so it not threadsafe 
     return "a string"; 
    } 
} 

避免明顯的事實,即一個平行環路會比在上述情況下的標準循環慢,我怎麼能寫Processor.DoStuffInParallel()方法,以避免對Worker類的當前硬依賴?脫鉤這一

+0

相關:https://stackoverflow.com/questions/13982600/using-dependencies-on-multiple-threads-with-parallel-foreach – Steven

回答

4

一種方式是通過注入工廠,例如:

public List<string> DoStuffInParallel(IWorkerFactory factory) 
{ 
    var list = new System.Collections.Concurrent.ConcurrentBag<string>(); 

    Parallel.For(0, 10, i => 
    { 
     list.Add(factory.Create().GetString()); 
    }); 
    return list.ToList(); 
} 

工廠可能是一個容器資單,而Create()需要是線程安全的。當然

請注意,您的任務不能同時變異列表 - 你會增加工人的結果時,列表需要同步訪問(apols,錯過了你ConcurrentBag) - 爲了減少對bag的爭用,在同步到localFinally中的聚合/整體包之前,您可能還需要查看Parallel.For過載之一localinit/localFinally以將結果本地聚合到每個任務列表中。

編輯
回覆:我需要注入一個工廠ConcurrentBag<String>? IMO,這可以直接創建ConcurrentBag - 它是一個實現特定的細節,而不是依賴項。例如單線程實現可能已經實現了這個:

return Enumerable.Range(0, 10) 
       .Select(i => factory.Create().GetString()) 
       .ToList(); 

即,沒有任何明確的中間容器結構。

您可以選擇軟化方法的接口到public IList<string> DoStuffInParallel甚至到IEnumerable<string>(可能的最小合同/承諾)。這裏的依賴關係在Worker上,這是你希望能夠在單元測試中模擬的東西。

+1

謝謝斯圖爾特。現在看看工廠。並行循環中的本地聚合是一個很好的調用 - 我懷疑併發包在這裏是過度殺毒(從許多線程寫入,從一個讀取)。由於接受答案傾向於殺死進一步的答案,所以會留下更長的時間,但我希望這將成爲我的解決方案。 – Steve

+1

此外,你會注意到我不在我的方法中注入名單。我覺得我不需要注入標準庫對象,這是一個合理的假設嗎? – Steve

+0

該假設沒有問題,除非它適用於將底層容器視爲依賴項的極端情況,而不僅僅是特定於實現的工件。如果您打算試驗/能夠從IoC控制容器類型,例如用'ConcurrentQueue','BlockingCollection'等來切換包 – StuartLC