c# – 使用Parallel.ForEach在多个线程上使用依赖项

我使用Simple Injector作为我的IoC容器. SimpleInjector使用 this simple technique to handle mixed life style for Per Thread and Per Web Request

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

不幸的是(显然!),在我的UnitOfWork类的其他地方,当我使用Parallel.ForEach并尝试并行调用Repository类的多个实例时,这给了我一个问题,因为只有第一个线程在HttpContext中找到一个值.当前

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

既然我已经写完了这个问题,我可以看到我可能会要求不可能或者是愚蠢的东西……但到底是什么……这可以做到吗?单个请求/线程注册是否可用于多个内部线程?

通过依赖注入,您可以尝试集中关于对象生命周期的知识.这个集中的地方称为 Composition Root.当您开始将依赖关系从一个线程传递到另一个线程时,代码的这些部分必须知道传递这些依赖关系是否安全.例如,那些依赖项是线程安全的吗?这些依赖项是否可以在新的上下文中运行(例如,在HTTP或WCF请求之外)?在许多情况下分析这可能是微不足道的,但是阻止您使用其他实现更改这些依赖项,因为现在您必须记住代码中有一个地方发生了这种情况,您需要知道传递了哪些依赖项.您正在再次分散这些知识,使得更难以推断DI配置的正确性,并且更容易错误配置容器,使您的代码直接失败(充其量)或导致难以调试竞争条件(最糟糕的情况) ).

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

在你的情况下,你甚至似乎遇到问题,因为你的存储库似乎与http上下文有关系,因为它们似乎不能传递给其他线程.在这种情况下,这很容易:不要这样做;-)

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

但是,在您的具体情况下,我发现您提交多个线程非常可怕.那个工作单元的提交不应该是原子的吗?当您在不同的线程上执行存储库提交时,您确定此提交仍然是原子的吗?当其中一个提交失败但其他提交成功时会发生什么?你如何回滚已经成功的操作?我认为你可以(而且你可能已经)解决了这些问题,但这种解决方案给系统增加了许多额外的复杂性.您必须认真地问自己,性能改进是否实际上会补偿​​您添加到系统中的额外复杂性.

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

相关文章
相关标签/搜索