【问题标题】:Hangfire BackgroundJob.Enqueue and method serializationHangfire BackgroundJob.Enqueue 和方法序列化
【发布时间】: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 谢谢。

标签: c# hangfire


【解决方案1】:

在测试中我可以看到私有成员 SomeClass._disposed 设置为 在 using 语句之后为 true,但是当 Hangfire 调用 SomeMethod 时 几秒钟后, SomeClass._disposed 设置为 false!这是如何 黑魔法发生了!?

因为两个x 不是同一个对象。 hangfire运行的那个是主程序运行的那个在入队时序列化的反序列化的结果。

在这种情况下,x.Dispose 可能会在作业运行之前被调用。 这会导致错误吗?

这可能会导致错误。例如,如果在与主程序相同的应用程序域中运行hangfire,并且如果释放x 也会释放一个共享资源,该资源将被hangfire 反序列化的x 使用。对我来说,处理作为排队操作一部分的对象似乎是一种代码味道,因为这些对象是可序列化的。

看看Enqueue 方法。您会注意到它采用Expression<Action> 作为参数(而不仅仅是Action)。 该表达式及其所有参数和常量将被序列化,并在 Hangfire 出列时被反序列化。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-12-11
    • 2012-11-07
    • 1970-01-01
    • 2010-11-19
    • 2022-10-13
    • 2019-05-03
    • 2014-10-01
    相关资源
    最近更新 更多