如何为指定后台任务限定静态类对象作用域,解决并发赋值覆盖问题
问题根因
你代码中使用static修饰的AppConfig.currentJob属于进程全局共享变量,和ASP.NET Core的ServiceScope生命周期完全无关。多个Hangfire后台任务并行执行时,后触发的任务赋值的job2会覆盖先触发任务赋值的job1,所以两个任务最终读取到的都是job2的值。
额外说明:原有代码存在两处拼写错误:字段_serviceScopFactory少写字母e,正确拼写为_serviceScopeFactory;读取job名时写的jobname大小写不匹配,属性定义为jobName,修改代码时请同步修正。
正确实现方案
根据不同的使用场景,可以选择以下三种方案:
- 方案1:移除全局静态变量,改为方法内局部变量存储job信息
适合job信息仅当前任务内部使用的场景,改造成本最低,修改后代码示例:// BackgroundJobService.cs修改示例 public async Task RunBackgroundJobTest1() { using (var serviceScope = _serviceScopeFactory.CreateScope()) { // 局部变量仅当前方法执行上下文可见,不会被其他任务覆盖 var currentJob = new BackgroundJob(){ jobName = "job1" }; var idx = 0; for (int i = 0; i <= 1000; i += 100) { idx++; await Task.Delay(1000); Console.WriteLine($"===> jobName = {currentJob.jobName}"); } } } // RunBackgroundJobTest2同理,把job存为方法内局部变量即可 - 方案2:用
AsyncLocal<T>实现异步上下文隔离的全局存储
适合需要在当前任务的整个异步调用链里传递job信息,不想每层都传参的场景。AsyncLocal的存储值会和异步执行上下文绑定,不同异步任务之间互不影响,修改示例:
赋值和读取时取Value属性即可:// AppConfig.cs修改 public class AppConfig { // AsyncLocal实现异步上下文隔离存储 public static AsyncLocal<BackgroundJob> currentJob { get; set; } = new(); }// 赋值 AppConfig.currentJob.Value = new BackgroundJob(){ jobName = "job1" }; // 读取 Console.WriteLine($"===> jobName = {AppConfig.currentJob.Value.jobName}"); - 方案3:把job信息注册为Scope级别的服务
适合依赖注入体系内,同一个Scope下的多个服务都需要用到当前job信息的场景,完全符合依赖注入生命周期规范。
首先在Program.cs中注册服务:
然后修改任务方法:builder.Services.AddScoped<BackgroundJob>();public async Task RunBackgroundJobTest1() { using (var serviceScope = _serviceScopeFactory.CreateScope()) { // 从当前Scope获取实例并赋值 var currentJob = serviceScope.ServiceProvider.GetRequiredService<BackgroundJob>(); currentJob.jobName = "job1"; // 后续Scope内的其他服务都可以注入BackgroundJob拿到当前任务的实例,不同Scope之间互不影响 var idx = 0; for (int i = 0; i <= 1000; i += 100) { idx++; await Task.Delay(1000); Console.WriteLine($"===> jobName = {currentJob.jobName}"); } } }
内容的提问来源于stack exchange,提问作者sambath999
相关产品推荐
相关产品推荐

