Недостаток очевиден, мы создаём таск на каждый элемент, и лучше использовать Parallel.ForEach.
Однако с помощью Parallel.ForEach невозможно получить количество элементов.
В вышеприведённом примере мы их получаем неявно за счёт использования List.
Как вариант можно хранить переменную и автономно увеличивать:
int count = 0;
Parallel.ForEach(enumerable, element =>
{
Interlocked.Increment(ref count);
Logger.Info($"Running task {element}");
});
Logger.Info($"Run {count} tasks");
Тогда мы получаем лишний расход в виде Interlocked.Increment и выведем лог только после того как вся работа окончится в отличии от начального кода.
_NN>Тогда мы получаем лишний расход в виде Interlocked.Increment и выведем лог только после того как вся работа окончится в отличии от начального кода.
Interlocked.Increment дешевле, чем таски создавать. И вообще Interlocked.Increment — довольно дешёвая штука. Если таски настолько короткие, что начинает играть роль лишний Interlocked.Increment, и вообще, если ты его замечаешь, то пора переходить к частным решениям конкретно для этого места в конкретном коде.
У IEnumerable enumerable нет Count, иначе не было бы вопроса.
А вызов Enumerable.Count() приведёт к двойному обходу перечисления, что тоже не подходит.
Здравствуйте, _NN_, Вы писали:
_NN>Как вы решаете эту задачу ?
Какую задачу?
В исходном коде до завершения обработки выводится отладочная инфа о числе созданных тасков (не знаю что это даёт правда).
В предложенном изменении сначала выполняется обработка всех элементов, а потом выводится в лог информация о тасках,
хотя вроде и смысл изменений в том, чтобы таски не создавать и по факту это просто число обрабатываемых элементов.
Задача в итоге чисто вывести что-то в лог или в чём-то другом?
Здравствуйте, _NN_, Вы писали:
_NN>У IEnumerable enumerable нет Count, иначе не было бы вопроса.
Что мешает добавить в свой интерфейс с количеством элементов.
Вы же это список заполняли как-то.
_NN>А вызов Enumerable.Count() приведёт к двойному обходу перечисления, что тоже не подходит.
Почему? Пока идёт выполнение ваших задач, что мешает пройтись по списку еще раз? (Кроме религиозных соображений)
Здравствуйте, kov_serg, Вы писали:
_>Здравствуйте, _NN_, Вы писали:
_NN>>У IEnumerable enumerable нет Count, иначе не было бы вопроса. _>Что мешает добавить в свой интерфейс с количеством элементов. _>Вы же это список заполняли как-то.
Ну к примеру у меня LINQ выражение какое-нибудь.
_NN>>А вызов Enumerable.Count() приведёт к двойному обходу перечисления, что тоже не подходит. _>Почему? Пока идёт выполнение ваших задач, что мешает пройтись по списку еще раз? (Кроме религиозных соображений)
Это если такая возможность есть.
Не каждый IEnumerable даст пройтись два раза без последствий.
Здравствуйте, _NN_, Вы писали:
_NN>Ну к примеру у меня LINQ выражение какое-нибудь.
И что вам мешает реализовать несколько интерфейсов?
_NN>Это если такая возможность есть. _NN>Не каждый IEnumerable даст пройтись два раза без последствий.
Это уже кривые реализации. В IEnumerable есть который должен переходить в начало.
IEnumerable.Reset
Здравствуйте, kov_serg, Вы писали:
_>Здравствуйте, _NN_, Вы писали:
_NN>>Ну к примеру у меня LINQ выражение какое-нибудь. _>И что вам мешает реализовать несколько интерфейсов?
Ну например у меня энумератор отправляет данные на сервер и ждёт ответа, а повторный проход даст другой результат.
А в руках у меня только IEnumerable.
Да что угодно может быть.
_>>И что вам мешает реализовать несколько интерфейсов?
_NN>Ну например у меня энумератор отправляет данные на сервер и ждёт ответа, а повторный проход даст другой результат.
Что мешает от сервера получить количество элементов? _NN>А в руках у меня только IEnumerable.
Сделайте копию. _NN>Да что угодно может быть.
Так и исключения можно кидать при переходе к следующему элементу.
Здравствуйте, _NN_, Вы писали:
_NN>Тогда мы получаем лишний расход в виде Interlocked.Increment и выведем лог только после того как вся работа окончится в отличии от начального кода.
Interlocked.Increment это вообще мелочи, можно пренебречь. Внутри Parallel.ForEach тоже свои Interlocked.Increment есть, если уж на то пошло.
По поводу лога — если уж очень хочется, чтобы логировал как можно раньше, то сделай враппер для своей enumerable, который на последнем MoveNext будет сразу счётчик в лог писать.
Здравствуйте, _NN_, Вы писали:
_NN>Ну например у меня энумератор отправляет данные на сервер и ждёт ответа, а повторный проход даст другой результат. _NN>А в руках у меня только IEnumerable. _NN>Да что угодно может быть.
С такой постановкой исходный вопрос вообще не имеет значения. Делай хоть так, хоть эдак — на скорость не влияет. И Interlocked.Increment, и создание тасков вместе с последующим GC, в обычном случае настолько быстрее, чем запрос к серверу на каждый enumerable, что разницу нельзя будет заметить.
Здравствуйте, kov_serg, Вы писали:
_NN>>У IEnumerable enumerable нет Count, иначе не было бы вопроса. _>Что мешает добавить в свой интерфейс с количеством элементов. _>Вы же это список заполняли как-то.
Это абстракция IEnumerable. Количество элементов никому не известно на момент получения первых элементов. Иначе это был бы ICollection.
_NN>>А вызов Enumerable.Count() приведёт к двойному обходу перечисления, что тоже не подходит. _>Почему? Пока идёт выполнение ваших задач, что мешает пройтись по списку еще раз? (Кроме религиозных соображений)
Начать с того, что это не список. Повторный проход по IEnumerable может вызвать повторный вызов SELECT * FROM, например. С совершенно иным результатом.
Всем спасибо за мысли и советы.
На данный момент проще привести всё заранее к массиву так как количество не превысит нескольких тысяч.
А вот создавать тысячи таксов это гораздо хуже.