项目
版本

Autofac 处理并发

Autofac 是为高度并发应用设计的。下面的指导将帮助你在这些场景中取得成功。

组件注册

ContainerBuilderComponentRegistryBuilder 不是线程安全的 ,并且仅在应用程序启动时使用单个线程。这是最常见的场景,适用于大多数应用。

服务解析

所有容器操作在多个线程之间是安全的。

为了减少锁定开销,每个 Resolve 操作都在一个提供了容器依赖解决功能的 "上下文" 中进行。这是传递给组件注册委托的参数。

上下文对象是单线程的 ,除依赖解析操作期间外,不应 使用它们。

避免将上下文存储在组件注册中:

// 这是错误的 - 不要这样做
builder.Register(c => new MyComponent(c));

在上述示例中,将 "c" 参数(类型为 IComponentContext)提供给了 MyComponent(它依赖于 IComponent)。这段代码是不正确的,因为临时的 "c" 参数会被重用。

相反,从 "c" 中获取 IComponentContext 来访问非临时上下文:

builder.Register(c =>
{
    IContext threadSpecificContext = c.Resolve<IComponentContext>(); // 访问真实上下文。
    return new MyComponent(threadSpecificContext);
}

还要注意不要初始化具有对 "c" 参数闭包的组件,因为任何 "c" 的重用都会导致问题。

容器层次结构机制进一步减少了锁定,通过维护任何工厂/容器组件的局部注册副本。一旦初始注册副本创建完毕,使用 "内" 容器的线程可以创建或访问此类组件,而不会阻塞其他线程。

生命周期事件

当使用可用的生命周期事件时,请不要在 PreparingActivatingActivated 事件处理器中回调到容器:而是使用提供的 IComponentContext

线程特定的服务

您可以使用 Autofac 注册特定于线程的服务。有关更多信息,请参阅 实例生命周期范围 页面。

内部机制

考虑到上述指导原则,这里提供了一些关于 Autofac 中线程安全和锁定的更具体信息。

线程安全类型

以下类型对多个线程并发访问是安全的:

  • Container
  • DisposerIDisposer 的默认实现)
  • LifetimeScopeILifetimeScope 的默认实现)

这些类型涵盖了几乎所有的运行时/解析场景。

以下类型旨在在配置时间用于单线程访问:

  • ContainerBuilder
  • ComponentRegistryBuilderIComponentRegistryBuilder 的默认实现)

因此,正确的 Autofac 应用程序会在启动时使用单个线程上的 ContainerBuilder 创建容器。后续使用容器可以在任何线程上进行。

避免死锁

Autofac 的设计方式使得在正常使用情况下不会发生死锁。这部分指南是为维护者或扩展编写者准备的。

可能会获得以下顺序的锁:

  • 拥有以下任何锁的线程可能无法获取更多锁:
    • Disposer
  • 拥有 LifetimeScope 锁的线程随后可以获取:
    • 其父 LifetimeScope 的锁
    • 上述列出的任何项目
在本文档中