public class MultithreadTester
{
public void Run()
{
var builder = new ContainerBuilder();
builder.RegisterType<ManualWork>().As<IWork>();
builder.RegisterType<ColabManualWork>().As<IColabWork>();
builder.RegisterType<RelaxAfterManualWork>().As<IRelax>();
var container = builder.Build();
//#1 - Simple single thread
using (var scope = container.BeginLifetimeScope())
{
var work = scope.Resolve<IWork>();
work.DoWork();
}
//#2 - Resolving dependecies in worker threads in scopes of these threads without passing lifetime scopes are container into implementation
using (var scope = container.BeginLifetimeScope())
{
var work = scope.Resolve<IColabWork>();
work.DoWork();
}
//#3 - Resolving dependecies in worker threads when original scope is already gone (simulates fast request on same service which spawns threads for request processing)
IColabWork workForSample3;
using (var scope = container.BeginLifetimeScope())
{
workForSample3 = scope.Resolve<IColabWork>();
}
workForSample3.DoWork();
Console.ReadKey();
}
public interface IRelax
{
void DoRelax();
}
public class RelaxAfterManualWork : IRelax
{
public void DoRelax()
{
Console.WriteLine("Relaxing after hard work...");
Thread.Sleep(1000);
Console.WriteLine("Relax is done...");
}
}
public interface IWork
{
void DoWork();
}
public class ManualWork : IWork
{
private readonly IRelax _relaxActivity;
public ManualWork(IRelax relaxActivity)
{
_relaxActivity = relaxActivity;
}
public void DoWork()
{
Console.WriteLine("Ufff, this is so hard...");
Thread.Sleep(5000);
Console.WriteLine("Work is done...");
_relaxActivity.DoRelax();
}
}
public interface IColabWork
{
void DoWork();
}
public class ColabManualWork : IColabWork
{
public void DoWork()
{
Console.WriteLine("We must discuss how to share the workload...");
Thread.Sleep(1500);
Action action = () =>
{
//IT WOULD BE FINE TO HAVE RESOLVED DEPENDENCIES PER THREAD AND IN THREAD OWN LIFETIMESCOPE
Console.WriteLine("Ufff, this is so hard but working with my buddies helps...");
Thread.Sleep(2500);
Console.WriteLine("Work is done...");
var relaxActivity = new RelaxAfterManualWork();
relaxActivity.DoRelax();
};
var thread1 = new Thread(() => { action(); });
var thread2 = new Thread(() => { action(); });
thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();
}
}
}
在标记为 #1 的示例中,我正在解析 IWork 并运行一些操作。对于单线程环境,我了解 DI 中发生了什么、应该如何使用 DI、lifetimescope 以及如何解决依赖关系。
但我很难理解多线程环境下的 DI。我尝试通过示例#2、#3 来演示我遇到的一些问题。在这些示例中,我需要以某种方式解决 LifetimeScope 中的依赖关系,该依赖关系将为 ColabManualWork 中的每个线程创建。当然,我不希望引用 Autofac 中的任何类来防止耦合。
我什至创建了一个简单的工厂,适合从当前的 LifetimeScope 创建嵌套的 LifetimeScope:
public interface IIsolatedLifetimeScopeFactory<TA>
{
void Create(Action<TA> action);
}
public class IsolatedLifetimeScopeFactory<TA> : IIsolatedLifetimeScopeFactory<TA>
{
private readonly ILifetimeScope _scope;
public IsolatedLifetimeScopeFactory(ILifetimeScope scope)
{
_scope = scope;
}
public void Create(Action<TA> action)
{
using (var subScope = _scope.BeginLifetimeScope())
{
var a = subScope.Resolve<TA>();
action(a);
}
}
}
但我不太喜欢这个解决方案。这里存在三个大问题 - 1)所有逻辑都必须在 lambda 函数(或等效方法)中; 2) 将来,如果再次处置父作用域,Autoflac 可以重新实现处置子作用域的功能(此功能已经存在几个月了); 3) 如示例#3 所示,我可以在 ColabManualWork 中的任何功能启动之前处置父 LifetimeScope,因此我的工厂将使用已处置的 LifetimeScope。
有人可以帮助我如何有效地解决工作线程中的问题吗?我读到了一些与 SimpleInjector 相关的内容在多线程应用程序中使用依赖注入 http://simpleinjector.readthedocs.org/en/latest/howto.html#multi-threaded-applications但我没有完全理解它,而且它与 Autofac 无关。那篇文章里写到在多线程应用程序中,每个线程都应该有自己的对象图。这意味着您通常应该在线程执行开始时调用一次 container.GetInstance() 来获取用于处理该线程的根对象
如何在不与 Autofac 耦合以及线程相关的生命周期范围内解决工作线程中的依赖关系?