Отрегулированное выполнение параллельной задачи в инструкции asyn c select со странным поведением - PullRequest
0 голосов
/ 18 июня 2020

В моем приложении я хочу добавить, скажем, 20 устройств, начиная с порта 10, используя функцию AddDeviceAsync, которая принимает 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

Примечание: неверно только имя устройства - устройства подключены к правильному порту.

Я подозреваю, что это каким-то образом связано с async closure в select, но я действительно не могу этого понять.

1 Ответ

1 голос
/ 18 июня 2020

Если нет причин объявлять 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.

Кроме того, использование ConfigureAwait(false) внутри лямбда излишне, поскольку нет любой риск тупиковой ситуации.

...