通过多线程(任务)运行方法的有效方法[重复]

Posted

技术标签:

【中文标题】通过多线程(任务)运行方法的有效方法[重复]【英文标题】:Efficient way to run method by multiple thread(task) [duplicate] 【发布时间】:2021-02-21 01:41:10 【问题描述】:

我正在寻找使用多线程运行方法的最佳方式。

这是我想运行的方法

public async Task<IActionResult> GenerateById(int id)

    //make a web request and save response to database using EF.
    return Json(new  success = true, message = "Worked" );

我需要为 ID 1 到 40 运行此方法,目前,它由 hangfire 完成,有 10 个作业。

在startup.cs中

recurringJobManager.AddOrUpdate("GenerateAll1",
                () => serviceProvider.GetService<IGenerator>().GenerateAll(1), "0 * * * *");
recurringJobManager.AddOrUpdate("GenerateAll2",
                () => serviceProvider.GetService<IGenerator>().GenerateAll(2), "0 * * * *");
recurringJobManager.AddOrUpdate("GenerateAll3",
                () => serviceProvider.GetService<IGenerator>().GenerateAll(3), "0 * * * *");
....
recurringJobManager.AddOrUpdate("GenerateAll10",
                () => serviceProvider.GetService<IGenerator>().GenerateAll(10), "0 * * * *");

这是GenerateAll

public async Task GenerateAll(int mod)

    if (mod == 10)
    
        mod = 0;
    
    List<DataSetting> listsToUpdate = _unitOfWork.DataSetting.GetAll(r => r.Id%10 == mod ).ToList();
    foreach (var list in listsToUpdate )
       
       await GenerateById(list.Id); 
    

所以每个作业处理 4 个 id(例如 ..GenerateAll1 为 Id 1、11、21 和 31 运行) 但我发现这是低效的。 因为当 GenerateById(11) 需要很长时间时,即使 GenerateAll2 作业已经完成,GenerateById(21) 和 GenerateById(31) 也不会运行,直到 GenerateById(11) 完成。

我想做的是像hangfire一样只运行1个工作

recurringJobManager.AddOrUpdate(
       "GenerateAll1",
                () => serviceProvider.GetService<IGenerator>().GenerateAll(), "0 * * * *");

GeneratAll() 方法创建 10 个线程,每个线程获取 Id 并运行 GenerateById 方法。 完成后,它会使用尚未生成的 id 运行 GenerateById。 所以所有 10 个线程都在工作,直到生成所有数据设置。

我可以就这种情况获得任何建议吗?

谢谢

【问题讨论】:

为什么不为每个 id 创建一个 Task,一次启动它们,然后只使用 await 整个集合?我想,这里没必要乱搞线程。 Parallel.For(0, 40, x =&gt; GenerateById(x)); 这样的东西有用吗? OP 需要提供更多信息。这是在 Asp.net 应用程序上吗?每个请求是否需要调用 `GenerateById(int id)` 40 次?等 【参考方案1】:

Parallel.For 对您尝试做的事情很有用。目前尚不清楚您对GenerateById() 的结果的意图是什么,所以我将把它留给您自己弄清楚。

来自文档:

执行一个 for 循环,其中迭代可以并行运行。

recurringJobManager.AddOrUpdate(
    "GenerateAll1",
        () => serviceProvider.GetService<IGenerator>().GenerateAll(), "0 * * * *");

public async Task GenerateAll()

    Parallel.For(0, _unitOfWork.DataSetting.Length,
        new ParallelOptions()  MaxDegreeOfParallelism = 10 ,
        x => GenerateById(_unitOfWork.DataSetting[x]).Result());


public async Task<IActionResult> GenerateById(int id)

    ...
    return Json(new  success = true, message = "Worked" );

【讨论】:

嗨,杰瑞,感谢您的建议。我应该提到方法 GenerateById()。它发出网络请求并将响应数据保存到数据库,所以它是由 EF 完成的。我得到一个错误,“在前一个操作完成之前,在这个上下文上开始了第二个操作”。您有什么想法可以与方法使用 EF 并行使用吗? 如果您希望所有线程访问同一个 DbContext 实例,那么您必须使用锁,这样一次只有一个线程在访问它。如果与您的网络请求相比,上下文的操作速度更快,那么并行执行此操作仍然会有很大的好处。

以上是关于通过多线程(任务)运行方法的有效方法[重复]的主要内容,如果未能解决你的问题,请参考以下文章

多线程高并发和多线程的关系

高并发和多线程的关系

高并发和多线程的关系

通过多线程分配 CoreLocation 和 CoreMotion 更新的最佳设计?

基于SmartThreadPool线程池技术实现多任务批量处理

Python通过多线程实现 `异步`