diff --git a/bench/Autofac.Benchmarks/ConcurrencyBenchmak.cs b/bench/Autofac.Benchmarks/ConcurrencyBenchmak.cs
new file mode 100644
index 000000000..051c4256c
--- /dev/null
+++ b/bench/Autofac.Benchmarks/ConcurrencyBenchmak.cs
@@ -0,0 +1,96 @@
+using BenchmarkDotNet.Attributes;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace Autofac.Benchmarks
+{
+ ///
+ /// Tests the performance of concurrent resolution of a (reasonably) deeply-nested object graph.
+ ///
+ public class ConcurrencyBenchmark
+ {
+ private readonly IContainer _container;
+
+ public ConcurrencyBenchmark()
+ {
+ var builder = new ContainerBuilder();
+ builder.RegisterType().SingleInstance();
+ builder.RegisterType();
+ builder.RegisterType();
+ builder.RegisterType();
+ builder.RegisterType();
+ builder.RegisterType();
+ builder.RegisterType();
+ _container = builder.Build();
+ }
+
+ [Params(10, 100, 1_000)]
+ public int ResolveTaskCount { get; set; }
+
+ [Params(1_000, 10_000)]
+ public int ResolvesPerTask { get; set; }
+
+ [Benchmark]
+ public void MultipleResolvesOnMultipleTasks()
+ {
+ var tasks = new List(ResolveTaskCount);
+
+ for (var i = 0; i < ResolveTaskCount; i++)
+ {
+ var task = Task.Run(() =>
+ {
+ try
+ {
+ for (var j = 0; j < ResolvesPerTask; j++)
+ {
+ var instance = _container.Resolve();
+ Assert.NotNull(instance);
+ }
+ }
+ catch (Exception ex)
+ {
+ Assert.True(false, ex.ToString());
+ }
+ });
+ tasks.Add(task);
+ }
+
+ Task.WhenAll(tasks);
+ }
+
+#pragma warning disable SA1402, SA1502
+
+ internal class A
+ {
+ public A(B1 b1, B2 b2) { }
+ }
+
+ internal class B1
+ {
+ public B1(B2 b2, C1 c1, C2 c2) { }
+ }
+
+ internal class B2
+ {
+ public B2(C1 c1, C2 c2) { }
+ }
+
+ internal class C1
+ {
+ public C1(C2 c2, D1 d1, D2 d2) { }
+ }
+
+ internal class C2
+ {
+ public C2(D1 d1, D2 d2) { }
+ }
+
+ internal class D1 { }
+
+ internal class D2 { }
+
+#pragma warning restore SA1402, SA1502
+ }
+}
diff --git a/bench/Autofac.Benchmarks/Harness.cs b/bench/Autofac.Benchmarks/Harness.cs
index d656a27a0..e9cd87c24 100644
--- a/bench/Autofac.Benchmarks/Harness.cs
+++ b/bench/Autofac.Benchmarks/Harness.cs
@@ -42,6 +42,12 @@ public void DeepGraphResolve()
BenchmarkRunner.Run();
}
+ [Fact]
+ public void Concurrency()
+ {
+ BenchmarkRunner.Run();
+ }
+
[Fact]
public void Decorator_Keyed_Generic()
{
diff --git a/src/Autofac/Core/Registration/ComponentRegistry.cs b/src/Autofac/Core/Registration/ComponentRegistry.cs
index fe594aeaa..e2696d1d1 100644
--- a/src/Autofac/Core/Registration/ComponentRegistry.cs
+++ b/src/Autofac/Core/Registration/ComponentRegistry.cs
@@ -29,6 +29,7 @@
using System.Diagnostics;
using System.Globalization;
using System.Linq;
+using System.Runtime.CompilerServices;
using Autofac.Builder;
using Autofac.Features.Decorators;
using Autofac.Util;
@@ -65,7 +66,7 @@ public class ComponentRegistry : Disposable, IComponentRegistry
///
/// Keeps track of the status of registered services.
///
- private readonly Dictionary _serviceInfo = new Dictionary();
+ private readonly ConcurrentDictionary _serviceInfo = new ConcurrentDictionary();
private readonly ConcurrentDictionary> _decorators
= new ConcurrentDictionary>();
@@ -118,9 +119,13 @@ public bool TryGetRegistration(Service service, out IComponentRegistration regis
{
if (service == null) throw new ArgumentNullException(nameof(service));
+ var info = GetInitializedServiceInfoOrDefault(service);
+ if (info != null && info.TryGetRegistration(out registration))
+ return true;
+
lock (_synchRoot)
{
- var info = GetInitializedServiceInfo(service);
+ info = GetInitializedServiceInfo(service);
return info.TryGetRegistration(out registration);
}
}
@@ -134,6 +139,10 @@ public bool IsRegistered(Service service)
{
if (service == null) throw new ArgumentNullException(nameof(service));
+ var info = GetInitializedServiceInfoOrDefault(service);
+ if (info != null && info.IsRegistered)
+ return true;
+
lock (_synchRoot)
{
return GetInitializedServiceInfo(service).IsRegistered;
@@ -233,9 +242,13 @@ public IEnumerable RegistrationsFor(Service service)
{
if (service == null) throw new ArgumentNullException(nameof(service));
+ var info = GetInitializedServiceInfoOrDefault(service);
+ if (info != null)
+ return info.Implementations.ToArray();
+
lock (_synchRoot)
{
- var info = GetInitializedServiceInfo(service);
+ info = GetInitializedServiceInfo(service);
return info.Implementations.ToArray();
}
}
@@ -366,17 +379,27 @@ private ServiceRegistrationInfo GetInitializedServiceInfo(Service service)
return info;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private ServiceRegistrationInfo GetServiceInfo(Service service)
{
- ServiceRegistrationInfo existing;
- if (_serviceInfo.TryGetValue(service, out existing))
+ if (_serviceInfo.TryGetValue(service, out var existing))
return existing;
var info = new ServiceRegistrationInfo(service);
- _serviceInfo.Add(service, info);
+ _serviceInfo.TryAdd(service, info);
return info;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private ServiceRegistrationInfo GetInitializedServiceInfoOrDefault(Service service)
+ {
+ if (_serviceInfo.TryGetValue(service, out var existing) && existing.IsInitialized)
+ return existing;
+
+ return null;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private EventHandler GetRegistered()
{
if (Properties.TryGetValue(MetadataKeys.RegisteredPropertyKey, out var registered))