Handling Concurrency
Autofac is designed for use in highly-concurrent applications. The guidance below will help you be successful in these situations.
Component Registration
ContainerBuilder
and ComponentRegistryBuilder
are not thread-safe and are designed to be used only on a single thread at the time the application starts up. This is the most common scenario and works for almost all applications.
Service Resolution
All container operations are safe for use between multiple threads.
To reduce locking overhead, each Resolve
operation takes place in a ‘context’ that provides the dependency-resolution features of the container. This is the parameter provided to component registration delegates.
Resolution context objects are single-threaded and should not be used except during the course of a dependency resolution operation.
Avoid component registrations that store the context:
// THIS IS BROKEN - DON'T DO IT
builder.Register(c => new MyComponent(c));
In the above example, the “c” IComponentContext
parameter is being provided to MyComponent (which takes IComponent
as a dependency). This code is incorrect because the temporary “c” parameter will be reused.
Instead resolve IComponentContext
from “c” to access the non-temporary context:
builder.Register(c =>
{
IContext threadSpecificContext = c.Resolve<IComponentContext>(); // access real context.
return new MyComponent(threadSpecificContext);
}
Take care also not to initialize components with closures over the “c” parameter, as any reuse of “c” will cause issues.
The container hierarchy mechanism further reduces locking, by maintaining local copies of the component registrations for any factory/container components. Once the initial registration copy has been made, a thread using an ‘inner’ container can create or access such components without blocking any other thread.
Lifetime Events
When making use of the LifetimeEvents available, don’t call back into the container in handlers for the Preparing
, Activating
or Activated
events: use the supplied IComponentContext
instead.
Thread Scoped Services
You can use Autofac to register services that are specific to a thread. The instance lifetime scope page has more information on this.
Internals
Keeping in mind the guidelines above, here’s a little more specific information about thread safety and locking in Autofac.
Thread-Safe Types
The following types are safe for concurrent access by multiple threads:
Container
Disposer
(default implementation ofIDisposer
)
LifetimeScope
(default implementation ofILifetimeScope
)
These types cover practically all of the runtime/resolution scenarios.
The following types are designed for single-threaded access at configuration time:
ContainerBuilder
ComponentRegistryBuilder
(default implementation ofIComponentRegistryBuilder
)
So, a correct Autofac application will use a ContainerBuilder
on a single thread to create the container at startup. Subsequent use of the container can occur on any thread.
Deadlock Avoidance
Autofac is designed in such a way that deadlocks won’t occur in normal use. This section is a guide for maintainers or extension writers.
Locks may be acquired in the following order:
A thread holding a lock for any of the following may not acquire any further locks:
Disposer
A thread holding the lock for a
LifetimeScope
may subsequently acquire the lock for:
Its parent
LifetimeScope
Any of the items listed above