依赖注入与并行处理 [英] Dependency injection with parallel processing

查看:103
本文介绍了依赖注入与并行处理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正尝试练习手动依赖注入(尚无框架)以消除代码中的紧密耦合。

到目前为止,简单的构造方法注入效果很好。



但是,当一个类必须在并行循环中使用另一个类时,我无法解决如何创建紧密耦合的问题。示例:

 公共类处理器
{
私人IWorker工作者;
公共处理器(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());
}
退货清单;
}

公共列表< string> DoStuffInParallel()
{
var list = new System.Collections.Concurrent.ConcurrentBag< string>();

Parallel.For(0,10,i =>
{
//是否有避免这种情况的简单方法?
list.Add(new Worker()。GetString());
});
return list.ToList();
}
}

public class Worker:IWorker
{
public string GetString()
{
//伪装成这个依赖于某些实例变量,因此它不是线程安全的
返回字符串;
}
}

避免明显的事实是并行循环会更慢与上述情况下的标准循环相比,我该如何编写 Processor.DoStuffInParallel()方法,以避免当前对 Worker的硬依赖性类?

解决方案

解耦的一种方法是注入工厂,例如:

 公共列表< 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 )-为了减少对的争用,您可能还想看看 Parallel.For 之一。用 localinit / localFinally 重载将结果本地汇总到每个任务列表中,然后再与 localFinally 中的汇总/整体包同步。



编辑

Re:我是否需要为 ConcurrentBag< String> 注入工厂?
IMO,可以直接创建 ConcurrentBag -这是实现特定的细节,而不是依赖项。例如单线程实现可能已实现为:

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



您可以选择将方法的接口软化为 public IList< string>。 DoStuffInParallel 或什至 IEnumerable< string> (可能的最低合同/承诺)。这里的依赖项是 Worker ,这是您希望能够在单元测试中模拟的内容。


I am trying to practice manual dependency injection (no framework yet) to remove tight coupling in my code. This is just for practice - I don't have a specific implementation in mind.

So far simple constructor injection has worked fine.

However I cannot work out how to solve creating a tight coupling when one class must use another within a parallel loop. Example:

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";
    }
}

Avoiding the obvious fact that a parallel loop will be slower than a standard loop in the above case, how could i write the Processor.DoStuffInParallel() method to avoid the current hard dependency on the Worker class?

解决方案

One way to decouple this is by injecting a factory, e.g.:

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();
}

The factory could be an container-owned singleton, and the Create() would need to be thread safe.

Note of course that your tasks can't concurrently mutate the list - you'll need to synchronize access when adding the worker result to the list (apols, missed your ConcurrentBag)- In order to reduce contention on the bag, you might also want to look at one of the Parallel.For overloads with localinit / localFinally to do a local aggregation of results into a per-task list, before synchronizing to the aggregated / overall bag in the localFinally.

Edit
Re: Do I need to inject a factory for ConcurrentBag<String> ? IMO, this is fine to create the ConcurrentBag directly - it is an implementation specific detail, rather than a dependency. e.g. a Single threaded implementation may have implemented this as :

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

i.e. without any explicit construction of the intermediate container.

You may choose to soften the interface to the method to public IList<string> DoStuffInParallel or even to IEnumerable<string> (the minimum possible contract / commitment). The dependency here is on the Worker, which is what you will want to be able to mock in unit testing.

这篇关于依赖注入与并行处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆