在异常行为的异步 select 语句中限制并行任务执行

Throttled parallel task execution in async select statement with strange behaviour

在我的应用程序中,我想通过使用函数 AddDeviceAsync 添加从端口 10 开始的 20 个设备,该函数将 template 作为参数,用于创建具有名称的实际设备template.Name 通过联系端口 port.

我正在使用 SemaphoreSlim 来防止通过同时添加到许多设备来耗尽系统。

using (var semaphore = new SemaphoreSlim(10))
{
    var tasks = Enumerable.Range(startPort, devicesCount).Select(
             async port =>
                        {
                           try
                            {
                                await semaphore.WaitAsync().ConfigureAwait(false);
                                template.Name = $"{device.Name} {port}";
                                template.CommunicationSettings.Port = port;
                                await this.equipmentNewApplicationService.AddDeviceAsync(template).ConfigureAwait(false);
                            }
                            finally
                            {
                                semaphore.Release();
                            }
                        });

    await Task.WhenAll(tasks).ConfigureAwait(false);
}

不过我的代码出了点问题,因为一旦开始节流 (deviceCount = 20)

一些设备共享它们的名称:

DeviceName-10
DeviceName-11
DeviceName-12
DeviceName-13
DeviceName-14
DeviceName-14
DeviceName-14
DeviceName-17

注意:只有设备名称错误 - 设备连接到正确的端口。

我怀疑这与 select 中的 async closure 有某种联系,但我实在想不通。

如果没有理由在外部范围内声明 template(而且似乎没有),则将其移入您的 Select:

using (var semaphore = new SemaphoreSlim(10))
{
    var tasks = Enumerable.Range(startPort, devicesCount).Select(async port =>
    {
        try
        {
            await semaphore.WaitAsync();

            var template = new Template
            {
                Name = $"{device.Name} {port}",
                CommunicationSettings.Port = port
            };
            await this.equipmentNewApplicationService.AddDeviceAsync(template);
        }
        finally
        {
            semaphore.Release();
        }
    });

    await Task.WhenAll(tasks).ConfigureAwait(false);
}

因为 Select 正在产生多个任务,可能 运行 在多个线程上,否则会出现尝试更新 template.

的竞争条件

此外,在 lambda 中使用 ConfigureAwait(false) 是多余的,因为没有任何死锁风险。