首页
/ Hangfire.InMemory 0.9.0版本中的对象释放异常问题解析

Hangfire.InMemory 0.9.0版本中的对象释放异常问题解析

2025-05-24 02:20:56作者:冯梦姬Eddie

问题背景

在使用Hangfire.InMemory存储组件时,从0.8.1版本升级到0.9.0版本后,用户在使用NUnit 4.1.0进行并发测试时遇到了ObjectDisposedException异常。具体表现为单个测试可以通过,但在并发执行时会在提交写事务时失败。

异常分析

异常堆栈显示,问题发生在InMemoryDispatcher组件中,提示"无法访问已释放的对象"。核心错误出现在事务提交阶段,当尝试执行查询和等待操作时,调度器已经被释放。

根本原因

Hangfire.InMemory 0.9.0版本中,InMemoryStorage类实现了IDisposable接口。这意味着存储实例现在需要显式管理其生命周期。在并发测试环境下,如果多个测试共享同一个静态存储实例(通过JobStorage.Current访问),当一个测试完成并触发Dispose后,其他并发运行的测试尝试访问已释放的资源就会抛出异常。

解决方案

正确的做法是为每个测试创建独立的InMemoryStorage实例,而不是依赖全局静态实例。这样可以确保:

  1. 每个测试都有自己独立的存储环境
  2. 测试之间不会相互干扰
  3. 资源生命周期与测试生命周期一致

最佳实践建议

  1. 测试隔离:在测试初始化时创建新的存储实例,在测试清理时释放它
  2. 避免静态依赖:不要依赖JobStorage.Current,而是显式传递存储实例
  3. 并发安全:确保每个并发测试线程都有自己的存储实例
  4. 版本升级注意:当依赖库引入IDisposable实现时,需要检查现有代码的资源管理方式

代码改进示例

对于问题中提到的创建Token的方法,可以考虑重构为接受存储实例参数:

public string CreateToken(JobStorage storage, TimeSpan expireIn)
{
    var tokenId = Guid.NewGuid().ToString("N");
    var resourceKey = "execution-tokens:" + tokenId;

    using var connection = storage.GetConnection();
    using var transaction = connection.CreateWriteTransaction();
    // 其余代码保持不变...
}

这样调用方可以控制存储实例的生命周期,更适合测试场景。

总结

Hangfire.InMemory 0.9.0引入的IDisposable实现是一个重要的改进,它要求开发者更明确地管理资源生命周期。在测试环境中,特别是并发测试场景下,必须确保每个测试都有独立的存储实例。这种改变虽然可能导致现有代码需要调整,但最终会带来更可靠的资源管理和更稳定的测试执行。

登录后查看全文
热门项目推荐
相关项目推荐