【发布时间】:2019-12-06 19:59:13
【问题描述】:
使用hangfire,我们可以执行以下操作:
public MyClass
{
public void RunJob()
{
SomeClass x = new SomeClass();
x.SomeProperty = "Test";
Hangfire.BackgroundJob.Enqueue(() => x.SomeMethod());
}
}
想象SomeMethod 不是静态的,它取决于 SomeClass.SomeProperty 的值。如果SomeMethod 不是静态的,这怎么能工作?对象x 将在两行之后超出范围。 Hangfire 是否会增加对对象 x 的引用计数,并且垃圾收集器在 Hangfire 完成之前不会清理该对象?
进一步,想象一下如果SomeClass 实现了 IDisposable 并且SomeMethod 依赖于 Dispose 清理的资源:
public MyClass
{
public void RunJob()
{
using (SomeClass x = new SomeClass())
{
x.SomeProperty = "Test";
Hangfire.BackgroundJob.Enqueue(() => x.SomeMethod());
}
}
}
在这种情况下,x.Dispose 可能会在作业运行之前被调用。这会导致错误吗?
在测试中,我可以看到私有成员 SomeClass._disposed 在 using 语句之后设置为 true,但是当 Hangfire 在几秒钟后调用 SomeMethod 时, SomeClass._disposed 设置为 false!这种黑魔法是怎么发生的!?
观察
我修改了类以包括:
public MyClass
{
public static int LastId = 0;
private static int Id = 0;
public MyClass()
{
Id = ++LastId;
}
public void RunJob()
{
using (SomeClass x = new SomeClass())
{
x.SomeProperty = "Test";
Hangfire.BackgroundJob.Enqueue(() => x.SomeMethod());
}
}
}
... 使用它我可以观察到 Hangfire 使用的类的 Id 与我的第一个示例中使用的实例不同。所以我假设 Enqueue 会复制对象 x,或者 () => 运算符会这样做。
【问题讨论】:
-
由于它正在工作,我只能看到一种可能的解释:Hangfire 序列化参数(在这种情况下包括
x)并将其存储在数据库中。当需要运行任务时,它会再次序列化,并创建一个新实例。 This 文档中的页面说这是对参数的处理方式 - 所以我猜x有资格作为参数(至少对于 Hangfire) -
我还没有阅读源代码 - 但是私有成员可以使用反射。所以这至少是他们使用的一种可能的解决方案。您必须通读source code 才能确定。
-
@fredrik 谢谢。