Hangfire BackgroundJob.Enqueue 和方法序列化
Hangfire BackgroundJob.Enqueue and method serialization
使用 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!这种黑魔法是怎么发生的!?
观察结果
我修改了 class 以包括:
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 使用的 class 的 Id
与我的第一个示例中使用的实例不同。所以我假设 Enqueue 制作了对象 x 的副本,或者 () => 运算符可能做了。
In testing I can see that private member SomeClass._disposed is set to
true following the using statement, but when Hangfire calls SomeMethod
seconds later, SomeClass._disposed is set to false! How does this
black magic happen!?
因为x
两个不是同一个对象。 hangfire的运行是主程序反序列化运行的结果,入队时已经序列化
In this case x.Dispose would likely be called before the job is run.
Does this cause a bug?
这可能会导致错误。例如,如果 运行 hangfire 与主程序在同一个应用程序域中,并且如果处置 x
也会处置一个共享资源,该资源将由 hangfire 反序列化的 x
使用。处理作为入队操作一部分的对象对我来说似乎是一种代码味道,因为这些对象应该是可序列化的。
看看Enqueue
方法。您会注意到它采用 Expression<Action>
作为参数(而不仅仅是 Action
)。
该表达式及其所有参数和常量将被序列化,并在 Hangfire 出队时被反序列化。
使用 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!这种黑魔法是怎么发生的!?
观察结果
我修改了 class 以包括:
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 使用的 class 的 Id
与我的第一个示例中使用的实例不同。所以我假设 Enqueue 制作了对象 x 的副本,或者 () => 运算符可能做了。
In testing I can see that private member SomeClass._disposed is set to true following the using statement, but when Hangfire calls SomeMethod seconds later, SomeClass._disposed is set to false! How does this black magic happen!?
因为x
两个不是同一个对象。 hangfire的运行是主程序反序列化运行的结果,入队时已经序列化
In this case x.Dispose would likely be called before the job is run. Does this cause a bug?
这可能会导致错误。例如,如果 运行 hangfire 与主程序在同一个应用程序域中,并且如果处置 x
也会处置一个共享资源,该资源将由 hangfire 反序列化的 x
使用。处理作为入队操作一部分的对象对我来说似乎是一种代码味道,因为这些对象应该是可序列化的。
看看Enqueue
方法。您会注意到它采用 Expression<Action>
作为参数(而不仅仅是 Action
)。
该表达式及其所有参数和常量将被序列化,并在 Hangfire 出队时被反序列化。