Когда мне следует использовать SemaphoreSlim в .NET?

У меня есть метод, который читает хэши из Redis:

private Task FetchHashesFromRedis(List<string> redisKeys, ConcurrentBag<LiveDataModel> liveDataModels,
 CancellationToken cancellationToken) 
{
     var parallelism = Environment.ProcessorCount;
     var semafore = new SemaphoreSlim(initialCount: parallelism, maxCount: parallelism);
     var tasks = new List<Task>();
     Parallel.ForEach(redisKeys, (key) =>
     {
          tasks.Add(ExecuteOne(key, semafore, liveDataModels, cancellationToken));
     });

     return Task.WhenAll(tasks);
}

redisKeys счетчик списка всегда равен 1000, поэтому он всегда будет делать тысячу запросов.

FetchHashesFromRedis метод всегда один и тот же

ExecuteOneметод в первом случае выглядит так:

private async Task ExecuteOne(string redisKey, SemaphoreSlim semafore, ConcurrentBag<LiveDataModel> liveDataModels,
CancellationToken cancellationToken)
{
     var liveData = await _getLiveDataFromRedis.ExecuteAsync(redisKey, cancellationToken);
     if (liveData != null)
     {
         liveDataModels.Add(liveData);
     }
}

В этом первом случае выполнение 1000 запросов к Redis требует 1,5 секунды со всей дополнительной работой, которую я выполняю с моделями, которые я получаю.

ExecuteOne метод во втором случае (с семафором) выглядит так:

private async Task ExecuteOne(string redisKey, SemaphoreSlim semafore, ConcurrentBag<LiveDataModel> liveDataModels,
CancellationToken cancellationToken)
{
     await semafore.WaitAsync(cancellationToken);
     try 
     {
           var liveData = await _getLiveDataFromRedis.ExecuteAsync(redisKey, cancellationToken);
           if (liveData != null)
           {
              liveDataModels.Add(liveData);
           }
     }
     finally
     {
       semafore.Release();
     }
}

Во втором случае выполнение 1000 запросов к Redis требует 4,5 секунды со всей дополнительной работой, которую я выполняю с моделями, которые я получаю. (то же количество запросов, что и в первом случае)

Итак, единственная разница между первым и вторым случаем, что во втором случае я использую это:

await semafore.WaitAsync(cancellationToken);

и в блоке finally я использую:

semafore.Release();

Почему при использовании semafore требуется больше времени (до 3 раз)? Должен ли я использовать semafore в этом случае или нет? И когда я должен использовать семафор?

ПРИМЕЧАНИЕ. Метод _getLiveDataFromRedis.ExecuteAsync(redisKey, cancellationToken); не является потокобезопасным, он просто считывает разные значения из Redis и возвращает LiveDataModel

это зависит от того, является ли getLiveDataFromRedis.ExecuteAsync потокобезопасным или нет

Selvin 17.05.2022 14:24

@ Selvin нет, это не так, он просто читает разные значения из redis и возвращает LiveDataModel

SValley_Dev 17.05.2022 14:28

это semaphore, а не semafore.

JHBonarius 17.05.2022 14:37

под потокобезопасностью я имею в виду, если его можно вызывать несколько раз одновременно ... во втором ExecuteOne единственная причина использовать semafore - это если ExecuteAsync нельзя вызывать несколько раз одновременно ... ConcurrentBag позаботьтесь о добавлении элементов в в то же время в liveDataModels

Selvin 17.05.2022 14:37

@JHBonarius не имеет значения, что некоторые языки используют «нормальный» f вместо «ненормальный» ph

Selvin 17.05.2022 14:38

Язык переполнения стека @Selvin - английский, как прямо указано в рекомендациях. Так что "ф" - это норма. (кроме того, термин составлен из древнегреческого φόρος, так что «фи» -> «ф»)

JHBonarius 17.05.2022 14:42

Я использую семафор в случае, когда я потенциально хочу вызывать внешний API тысячи раз, и этот API начнет дросселировать, когда будет получено более 100 параллельных запросов. Я запускаю первые 100 параллельных вызовов, и как только вызов возвращается, я отправляю следующий запрос. Могут быть и другие способы обработки такой логики, но использование семафора было для меня относительно простым.

grek40 17.05.2022 15:48
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
Четыре эффективных способа центрирования блочных элементов в CSS
Четыре эффективных способа центрирования блочных элементов в CSS
У каждого из нас бывали случаи, когда нам нужно отцентрировать блочный элемент, но мы не знаем, как это сделать. Даже если мы реализуем какой-то...
2
7
51
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Why when I use semafore it takes more time (up to 3 times more)?

Вероятно, потому что это ограничивает количество одновременных операций ввода-вывода. Использование семафора ограничит количество одновременных вызовов количеством процессоров, однако, поскольку он включает ввод-вывод, большую часть времени будет просто ожидание, процессорное время не требуется. Таким образом, ограничение параллелизма количеством ядер не имеет большого смысла. Попробуйте увеличить maxCount и посмотрите, поможет ли это производительности.

Should I use semafore in this case or no?

Поскольку это медленнее и, похоже, не требуется по какой-либо причине безопасности потоков, ответ, вероятно, будет «Нет».

And when should I use semafore?

Я редко использую семафоры. Самая веская причина, которую я знаю, это если мне нужна асинхронная блокировка, то есть семафор с maxcount 1. Для него есть специальные варианты использования, но в большинстве случаев я нахожу примитивы более высокого уровня, которые заботятся о синхронизации, более полезными.

Я мог бы предложить прочитать Поток данных, который может позволить вам настроить конвейер асинхронной обработки, который лучше подходит для вашего варианта использования.

Другие вопросы по теме