【问题标题】:How to scope static class object for specific background task?如何为特定的后台任务确定静态类对象的范围?
【发布时间】:2021-12-26 11:14:18
【问题描述】:

考虑

public class BackgroundJob{
   public string jobName{get;set;}
   ...
}

AppConfig.cs

public class AppConfig{
   public static BackgroundJob currentJob{get;set;}
}

BackgroundjobService.cs

public interface IBackgroundJobService
{ ... }

public class BackgroundJobService : IBackgroundJobService
{
    private readonly ServiceScopFactory _serviceScopFactory;
    public BackgroundJobService(ServiceScopFactory serviceScopFactory)
    {
        _serviceScopFactory = serviceScopFactory;
    }
    public async Task RunBackgroundJobTest1()
    {
        using (var serviceScope = _serviceScopeFactory.CreateScope())
        {
            AppConfig.currentJob = new(){ jobName = "job1" };

                var idx = 0;
                for (int i = 0; i <= 1000; i += 100)
                {
                    idx++;
                    await Task.Delay(1000);
                    Console.WriteLine($"===> jobName = {currentJob.jobname}");
                }
        }
    }

    public async Task RunBackgroundJobTest2()
    {
        using (var serviceScope = _serviceScopeFactory.CreateScope())
        {
            AppConfig.currentJob = new(){ jobName = "job2" };

                var idx = 0;
                for (int i = 0; i <= 1000; i += 100)
                {
                    idx++;
                    await Task.Delay(1000);
                    Console.WriteLine($"===> jobName = {currentJob.jobname}");
                }
        }
    }
}

执行:

public class TestController : Controller
{
   private readonly IBackgroundJobClient _backgroundJobClient; //hangfire
   public TestController(IBackgroundJobClient backgroundJobClient)
   {
      _backgroundJobClient = backgroundJobClient;
   }
   [HttpGet("test")]
   public IActionResult Test()
   {
       _backgroundJobClient.Enqueue<IBackgroundJobService>(x=>x.RunBackgroundJobTest1()); //log ===> jobName = job2
       _backgroundJobClient.Enqueue<IBackgroundJobService>(x=>x.RunBackgroundJobTest2()); //log ===> jobName = job2
   }
}

因此,AppConfig.currentJob 不会保留ServiceScop1 内部/内部的第一个值,而是由ServiceScope2 内部的最后一个任务重新初始化
问题: 有人建议我为此目的正确实施吗?
我们将不胜感激。谢谢

【问题讨论】:

  • 如何为特定后台任务设置静态类对象的范围 - 不要使用static,而是为每个范围创建新实例。
  • 可能使用[ThreadStatic] 解决它,但是如果任务调度程序在同一个线程上连续运行作业,这将很难找到错误。您要求正确的实现,但我认为您的 BackgroundJobService 也应该受到审查。为什么你自己滚动而不使用IHostedService
  • @Fabio 感谢您的快速响应,但我们需要将其用作全局,这意味着在 serviceScope 内调用的其他服务也可以访问此object,而无需将参数作为参数传递给被调用的那些。
  • @CodeCaster 谢谢先生。由于我对长时间运行任务的后台作业的了解仍然有限,因此在您告诉我 IHostedService 之前,我已经阅读了有关使用 ServiceScopeFactory 的信息。当然,我需要正确的实施,很高兴收到您的建议。

标签: c# .net-core .net-5 hangfire background-task


【解决方案1】:

我不清楚您要达到的目标。无论如何,据我了解您的问题,您可以尝试下面的代码。 它将使 currentJob 本地化到您的处理中。与 [ThreadStatic] 不同,如果进程更改线程,这不会失败:

public class AppConfig{
   private static System.Threading.AsyncLocal<BackgroundJob> _currentJob {get;} 
        = new System.Threading.AsyncLocal<BackgroundJob>();

 public static BackgroundJob currentJob {
     get {return _currentJob.Value;}
     set {_currentJob.Value = value;}
  }   
}

请注意,此行为未链接到 ScopeServiceFactories。要拥有完整的依赖注入,链接到你需要删除静态的作用域。

【讨论】:

  • 谢谢!我们很抱歉您的回答迟了一点。对于这个问题,即我们正在测试长时间运行的任务,在运行时我们认为我们应该将objects 存储在当前运行范围global object 中为jobNameuserId,...并在每个服务中使用此信息在scope 中被调用,可能类似于AppConfig.currentJob.jobname.userId,因为需要一些表来标记用户日志信息。
  • 我们已经测试过这个解决方案并且它确实有效。但是要保证这种行为是否正确,我们是否需要,我们的知识仍然有限。我们已尝试将 IHostedService 作为问题的 cmets。但是您的回答确实对我们有帮助,非常感谢。
  • @sambath999:如果你需要它用于日志记录,那么我推荐ILogger.BeginScope
猜你喜欢
  • 2012-03-21
  • 2016-03-14
  • 1970-01-01
  • 2011-03-11
  • 1970-01-01
  • 1970-01-01
  • 2012-08-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多