繁体   English   中英

使用 SemaphoreSlim 进行分批执行

[英]Using SemaphoreSlim for divide execution for batches

我正在从头开始学习异步编程,并且必须解决一个问题。 我正在开发的应用程序必须按 ID 循环下载数据(大约 800 次循环通过)。 每个 Loop 循环从数据库中获取 10 到 500 行,并生成一个包含行的 txt 文件。 我想异步执行此操作。 当然不想同时生成 800 个报告(800 个 sql 查询)但想将它分成几批。 我使用SemaphoreSlim

public async void Generate(DateTime BusinessDate)
{
var throttler = new SemaphoreSlim(5);
                var allTasks = new List<Task>();
                foreach (var id in idsToGenerate)
                {
                    await throttler.WaitAsync();
                    allTasks.Add(Task.Run(async () =>
                    {          
                            GenerateReportForIdAsync(id, BusinessDate);
                    }));
                    throttler.Release();
                }
                
                await Task.WhenAll(allTasks);
}

private Task GenerateReportForIdAsync(Id id, DateTime day)
        {
            return Task.Run(() => GenerateReportForOid(id, day));
        }


private void GenerateReportForId(Id id, DateTime day)
        {
            LogInformation(); // shortcut
            GetDataFromDB(); // shortcut
            CreateReportFromRecordFromDB(); // shortcut
            UpdateInformationInDBThatReportHasBeenGenerated(); // shortcut
        }

此代码有效,因此已生成报告,但看起来方法UpdateInformationInDBThatReportHasBeenGenerated()尚未针对最后 1-10 个报告运行(取决于执行)。 因此,对于某些记录,看起来应用程序在执行UpdateInformationInDBThatReportHasBeenGenerated()之前已完成。

你知道为什么吗?

更新这是有效的:

public void Generate(DateTime BusinessDate)
{
      var taskAsync = GenerateAsync(BusinessDate);
            Task.WaitAll(taskAsync);
}

private async Task GenerateAsync(DateTime BusinessDate)
{
    var ab = new ActionBlock<(Id id, DateTime businessDate)>(
        GenerateReportForId,
        new ExecutionDataflowBlockOptions
        {
            MaxDegreeOfParallelism = 5,
        }
    );
    
    foreach (var id in idsToGenerate)
    {
        ab.Post((id, BusinessDate));
    }
    
    ab.Complete();
    
    await ab.Completion;
}

private void GenerateReportForId((Id id, DateTime day) arg)
{
    LogInformation(); // shortcut
    GetDataFromDB(); // shortcut
    CreateReportFromRecordFromDB(); // shortcut
    UpdateInformationInDBThatReportHasBeenGenerated(); // shortcut
}

您可以使用来自Dataflow(任务并行库)ActionBlock<TInput> Class轻松做到这一点:

public async Task Generate(DateTime BusinessDate)
{
    var ab = new ActionBlock<(Id id, DateTime businessDate)>(
        GenerateReportForId,
        new ExecutionDataflowBlockOptions
        {
            MaxDegreeOfParallelism = 5,
        }
    );
    
    foreach (var id in idsToGenerate)
    {
        ab.Post((id, BusinessDate));
    }
    
    ab.Complete();
    
    await ab.Completion;
}

private void GenerateReportForId((Id id, DateTime day) arg)
{
    LogInformation(); // shortcut
    GetDataFromDB(); // shortcut
    CreateReportFromRecordFromDB(); // shortcut
    UpdateInformationInDBThatReportHasBeenGenerated(); // shortcut
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM