使用并行的多个线程依赖



我使用简单的喷油器作为我的IOC容器。SimpleInjector使用此简单技术来处理每个线程和每个Web请求的混合生活方式

container.RegisterPerWebRequest<IWebRepository, Repository>();
container.RegisterLifetimeScope<IThreadRepository, Repository>();
container.Register<IRepository>(container.GetInstance<Repository>());
// Register as hybrid PerWebRequest / PerLifetimeScope.
container.Register<Repository>(() =>
{
    Repository repository;
    if (HttpContext.Current != null)
        repository = (Repository)container.GetInstance<IWebRepository>();
    else
        repository = (Repository)container.GetInstance<IThreadRepository>();
    return repository;
});

不幸的是(显然!),在我的单位工程类中的其他地方,当我使用并行时,这给我一个问题。在httpcontext.current

using (TransactionScope scope = new TransactionScope())
{
    Parallel.ForEach(new List<IRepository>() { _repository1, _repository2 ... }, 
        (repository) =>
        {
            repository.Commit();
        });
    scope.Complete();
}

现在我已经完成了一个问题,我可以看到我可能会要求不可能或愚蠢的东西...但是到底是什么...这可以做到吗?可以向多个内部线程提供单个请求/线程注册吗?

随着依赖的注入,您尝试集中有关对象寿命的知识。这个集中位置称为组成根。当您开始将依赖关系从一个线程传递到另一个线程时,代码的那些部分必须知道是否可以安全地传递这些依赖关系。例如,这些依赖关系线程是否安全?这些依赖关系可以在该新上下文(HTTP或WCF请求之外)运行吗?在许多情况下进行分析可能是微不足道的,但是可以防止您使用其他实现来改变这些依赖关系,因为现在您必须记住,代码中存在一个发生的位置,并且您需要知道传递了哪些依赖关系。您正在再次分散这些知识,使您更难理解DI配置的正确性,并以使您的代码直接失效(充其量)或导致难以挑选的竞赛条件的方式更容易地对容器进行错误调整(最坏的情况)。

因此,让每个新启动的线程通过向容器询问容器来构建新对象图是最安全的。不要传递依赖关系(这意味着:由容器管理和注入的实例)从一个线程到另一个线程。

在您的情况下,您似乎甚至遇到了问题,因为您的存储库似乎与HTTP上下文有关系,因为它们似乎不是其他线程可以传播的。在这种情况下,这很"容易":不要这样做; - )

这并不意味着您不能以任何方式进行多线程来加快性能。但是,您必须确保此操作不会将依赖项从一个线程转移到线程,或者何时将其移动;确保此代码(在您的情况下,Parallel.ForEach)位于您应用程序的组成根部内,因为这是唯一可以/应该知道此操作是否安全执行的地方。

在您的特定情况下,我发现您要在多个线程上投入非常可怕。那个工作单位的承诺不应该是原子吗?您确定在执行不同线程的存储库时,此提交仍然是原子吗?当一项提交失败但其他人成功时会发生什么?您如何回滚已经成功的操作?

我认为您可以(而且您可能已经)解决了这些问题,但是这种解决方案为系统增加了很多复杂性。您必须问自己,绩效提高是否会增加复杂性。

您可以在此处找到有关在多线程应用程序中使用依赖项注入的更多信息。

最新更新