Autofac 处理并发
Autofac 是为高度并发应用设计的。下面的指导将帮助你在这些场景中取得成功。
组件注册
ContainerBuilder
和 ComponentRegistryBuilder
不是线程安全的 ,并且仅在应用程序启动时使用单个线程。这是最常见的场景,适用于大多数应用。
服务解析
所有容器操作在多个线程之间是安全的。
为了减少锁定开销,每个 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" 的重用都会导致问题。
容器层次结构机制进一步减少了锁定,通过维护任何工厂/容器组件的局部注册副本。一旦初始注册副本创建完毕,使用 "内" 容器的线程可以创建或访问此类组件,而不会阻塞其他线程。
生命周期事件
当使用可用的生命周期事件时,请不要在 Preparing
、Activating
或 Activated
事件处理器中回调到容器:而是使用提供的 IComponentContext
。
线程特定的服务
您可以使用 Autofac 注册特定于线程的服务。有关更多信息,请参阅 实例生命周期范围 页面。
内部机制
考虑到上述指导原则,这里提供了一些关于 Autofac 中线程安全和锁定的更具体信息。
线程安全类型
以下类型对多个线程并发访问是安全的:
Container
Disposer
(IDisposer
的默认实现)LifetimeScope
(ILifetimeScope
的默认实现)
这些类型涵盖了几乎所有的运行时/解析场景。
以下类型旨在在配置时间用于单线程访问:
ContainerBuilder
ComponentRegistryBuilder
(IComponentRegistryBuilder
的默认实现)
因此,正确的 Autofac 应用程序会在启动时使用单个线程上的 ContainerBuilder
创建容器。后续使用容器可以在任何线程上进行。
避免死锁
Autofac 的设计方式使得在正常使用情况下不会发生死锁。这部分指南是为维护者或扩展编写者准备的。
可能会获得以下顺序的锁:
- 拥有以下任何锁的线程可能无法获取更多锁:
Disposer
- 拥有
LifetimeScope
锁的线程随后可以获取:- 其父
LifetimeScope
的锁 - 上述列出的任何项目
- 其父