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 出队时被反序列化。