Autofac 为新线程创建子范围未按预期工作 "Instances cannot be resolved and nested lifetimes cannot be created..."
Autofac Creating Child Scope for New Thread not working as expected "Instances cannot be resolved and nested lifetimes cannot be created..."
当有人调用 URL“/index/5”时,我想 return 一个视图,同时我想启动一个新线程,我想在其中弄清楚通过一些数据库调用和业务逻辑,我是否应该向其他人发送通知。这是我的设置的简化表示,它给出了一个错误。
如何让我的子作用域在并行线程中工作?
应用启动
var builder = new ContainerBuilder();
builder.RegisterType<MyRepository>().As<IMyRepository();
builder.RegisterType<Entities>().As<Entities>().InstancePerRequest();
var container = builder.Build();
控制器
private readonly IMyRepository _myRepository ;
public MyController(
IMyRepository myRepository
)
{
_myRepository = myRepository;
}
public async Task<ActionResult> Index(int id)
{
_myRepository.DoSomething(id);
return View();
}
存储库:
private ILifetimeScope _lifeTimeScopeChild = null;
public void DoSomething(int id){
//start new thread with child scope
using(var threadLifeTime = AutofacDependencyResolver.Current.ApplicationContainer.BeginLifetimeScope())
{
_lifeTimeScopeChild = threadLifeTime;
Thread t = new Thread(new ParameterizedThreadStart(MySeparateThread));
t.Start(id);
}
}
private void MySeparateThread(object id) {
var _entities = _lifeTimeScopeChild.Resolve<Entities>(); //IT CRASHES HERE
}
错误:
Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it has already been disposed.
我想要完成的事情:https://autofaccn.readthedocs.io/en/latest/lifetime/instance-scope.html#thread-scope
这里的关键部分不是错误消息的第一部分,而是最后一部分:
it has already been disposed.
using(var threadLifeTime = AutofacDependencyResolver.Current.ApplicationContainer.BeginLifetimeScope())
{
_lifeTimeScopeChild = threadLifeTime;
Thread t = new Thread(new ParameterizedThreadStart(MySeparateThread));
t.Start(id);
}
threadLifeTime
是在 using
块的声明中创建的。在该块的末尾,它被处理掉。这是 using
块的唯一目的。有一个内置的假设,即此时可以处置对象。大功告成。
您还创建了一个单独的线程并将 threadLifeTime
传递给它。发生这种情况的代码部分没有显示,但这就是正在发生的事情。您可能没有明确传递它,但它在 ParameterizedThreadStart
和 MySeparateThread
.
中的某处被引用
该线程继续独立于最初调用它的方法执行。因此,在您交出该对象后,它会立即被处理掉。无论该线程在做什么,它都会尝试使用已处置的对象来完成。那是错误。
通常在 using
块的末尾,变量 (threadLifeTime
) 会超出范围。不会有对它的引用,所以它是否被处置并不重要。但是现在有对它的引用,这是一个问题,因为它是对已被处置的东西的引用。
短期解决方案是不创建单独的线程。如果有一个涉及多线程的正确答案,它会更复杂并且超出这个答案的范围。一个好的经验法则是首先让代码在没有多线程的情况下工作,然后在需要时添加它。否则你不知道问题是多线程还是别的什么。
另一个问题是:
_lifeTimeScopeChild = threadLifeTime;
这表明它不仅被传递给其他线程,而且还被分配给 class 中的一个字段。出于完全相同的原因,这也是一个问题。即使在对象被释放后,您分配给该字段的引用仍然存在。如果在此 using
块完成后有任何尝试使用 _lifeTimeScopeChild
,它将得到相同的错误。
要回答的问题是 "Does my method need this object or does my class need this object?"
如果您的方法需要它,则在方法中声明并使用它,但不允许从方法中对您无法控制的任何引用 "escape"。如果你处置它,那么你会破坏任何其他试图使用它的东西。如果你不处理它,那么它就不会在应该处理的时候处理。
如果您的 class 需要它,请考虑在创建 class 时创建它,或者在需要时使用延迟实例化来创建它。 (我会从前者开始。)然后让你的 class 实现 IDisposable
,当你的 class 被处置时,也就是你处置你的 [=52] 使用的任何一次性资源的时候=].
当有人调用 URL“/index/5”时,我想 return 一个视图,同时我想启动一个新线程,我想在其中弄清楚通过一些数据库调用和业务逻辑,我是否应该向其他人发送通知。这是我的设置的简化表示,它给出了一个错误。
如何让我的子作用域在并行线程中工作?
应用启动
var builder = new ContainerBuilder();
builder.RegisterType<MyRepository>().As<IMyRepository();
builder.RegisterType<Entities>().As<Entities>().InstancePerRequest();
var container = builder.Build();
控制器
private readonly IMyRepository _myRepository ;
public MyController(
IMyRepository myRepository
)
{
_myRepository = myRepository;
}
public async Task<ActionResult> Index(int id)
{
_myRepository.DoSomething(id);
return View();
}
存储库:
private ILifetimeScope _lifeTimeScopeChild = null;
public void DoSomething(int id){
//start new thread with child scope
using(var threadLifeTime = AutofacDependencyResolver.Current.ApplicationContainer.BeginLifetimeScope())
{
_lifeTimeScopeChild = threadLifeTime;
Thread t = new Thread(new ParameterizedThreadStart(MySeparateThread));
t.Start(id);
}
}
private void MySeparateThread(object id) {
var _entities = _lifeTimeScopeChild.Resolve<Entities>(); //IT CRASHES HERE
}
错误:
Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it has already been disposed.
我想要完成的事情:https://autofaccn.readthedocs.io/en/latest/lifetime/instance-scope.html#thread-scope
这里的关键部分不是错误消息的第一部分,而是最后一部分:
it has already been disposed.
using(var threadLifeTime = AutofacDependencyResolver.Current.ApplicationContainer.BeginLifetimeScope())
{
_lifeTimeScopeChild = threadLifeTime;
Thread t = new Thread(new ParameterizedThreadStart(MySeparateThread));
t.Start(id);
}
threadLifeTime
是在 using
块的声明中创建的。在该块的末尾,它被处理掉。这是 using
块的唯一目的。有一个内置的假设,即此时可以处置对象。大功告成。
您还创建了一个单独的线程并将 threadLifeTime
传递给它。发生这种情况的代码部分没有显示,但这就是正在发生的事情。您可能没有明确传递它,但它在 ParameterizedThreadStart
和 MySeparateThread
.
该线程继续独立于最初调用它的方法执行。因此,在您交出该对象后,它会立即被处理掉。无论该线程在做什么,它都会尝试使用已处置的对象来完成。那是错误。
通常在 using
块的末尾,变量 (threadLifeTime
) 会超出范围。不会有对它的引用,所以它是否被处置并不重要。但是现在有对它的引用,这是一个问题,因为它是对已被处置的东西的引用。
短期解决方案是不创建单独的线程。如果有一个涉及多线程的正确答案,它会更复杂并且超出这个答案的范围。一个好的经验法则是首先让代码在没有多线程的情况下工作,然后在需要时添加它。否则你不知道问题是多线程还是别的什么。
另一个问题是:
_lifeTimeScopeChild = threadLifeTime;
这表明它不仅被传递给其他线程,而且还被分配给 class 中的一个字段。出于完全相同的原因,这也是一个问题。即使在对象被释放后,您分配给该字段的引用仍然存在。如果在此 using
块完成后有任何尝试使用 _lifeTimeScopeChild
,它将得到相同的错误。
要回答的问题是 "Does my method need this object or does my class need this object?"
如果您的方法需要它,则在方法中声明并使用它,但不允许从方法中对您无法控制的任何引用 "escape"。如果你处置它,那么你会破坏任何其他试图使用它的东西。如果你不处理它,那么它就不会在应该处理的时候处理。
如果您的 class 需要它,请考虑在创建 class 时创建它,或者在需要时使用延迟实例化来创建它。 (我会从前者开始。)然后让你的 class 实现 IDisposable
,当你的 class 被处置时,也就是你处置你的 [=52] 使用的任何一次性资源的时候=].