并行处理的dependency injection
我正在尝试练习手动dependency injection(还没有框架)来删除我的代码中的紧耦合。 这只是为了练习 – 我没有考虑具体的实现。
到目前为止,简单的构造函数注入工作正常。
但是当一个类必须在并行循环中使用另一个类时,我无法解决如何解决创建紧耦合的问题。 例:
public class Processor { private IWorker worker; public Processor(IWorker worker) { this.worker = worker; } public List DoStuff() { var list = new List(); for (int i = 0; i < 10; i++) { list.Add(worker.GetString()); } return list; } public List DoStuffInParallel() { var list = new System.Collections.Concurrent.ConcurrentBag(); 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
类的硬依赖?
解决这个问题的一种方法是注入工厂,例如:
public List DoStuffInParallel(IWorkerFactory factory) { var list = new System.Collections.Concurrent.ConcurrentBag (); Parallel.For(0, 10, i => { list.Add(factory.Create().GetString()); }); return list.ToList(); }
工厂可能是一个容器拥有的单例,而Create()
需要是线程安全的。
请注意,您的任务不能同时改变列表 – 在将工作结果添加到列表时需要同步访问权限 ( apols,错过了你的ConcurrentBag
) – 为了减少对bag
争用,你可能还想用localinit / localFinally查看其中一个Parallel.For
重载, 以便在每个任务列表中进行本地的结果聚合。同步到localFinally
的聚合/整体包。
编辑
Re:我是否需要为ConcurrentBag
注入工厂? IMO,这可以直接创建ConcurrentBag
– 它是一个特定于实现的细节,而不是依赖。 例如,单线程实现可能已将其实现为:
return Enumerable.Range(0, 10) .Select(i => factory.Create().GetString()) .ToList();
即没有任何明确的中间容器结构。
您可以选择将方法的接口软化为public IList
,甚至是IEnumerable
(最小可能的合同/承诺)。 这里的依赖关系是Worker
,这是你希望能够在unit testing中模拟的。