2014-06-20 71 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