Задачи не являются потоками. «Асинхронный» не означает «одновременный».
Что не так с моим кодом здесь или я что-то неправильно понимаю?
Вы не понимаете, какие задачи .
Вы должны думать о задачах как о чем-то, что вы можете делать в любом порядке. Возьмите пример кулинарного рецепта:
- Нарезать картофель
- Нарезать овощи
- Нарезать мясо
Если бы это было не задачи, и это был синхронный код, вы всегда выполняете эти шаги в точном порядке, в котором они перечислены.
Если бы они были задачами, это не значит, что эти работы будут выполняться одновременно. Вы - только один человек (= один поток), и вы можете делать только одну вещь за один раз.
Вы можете выполнять задачи в любом порядке, который вам нравится, вы можете даже остановить одну задачу, чтобы начать с другой, но вы до сих пор не может сделать больше, чем одну вещь одновременно. Независимо от порядка, в котором вы выполняете задачи, общее время, затрачиваемое на выполнение всех трех задач, остается неизменным, и это (по сути) не быстрее.
Если бы они были потоками, это все равно, что нанимать 3 шеф-поваров, что означает, что эти задания можно выполнять одновременно.
Асинхронность сокращает время простоя, когда оно ожидается .
Обратите внимание, что асинхронный код может привести к выигрышу во времени в тех случаях, когда ваш синхронный код в противном случае работал бы вхолостую, например, ожидание ответа сети. Это не учитывается в приведенном выше примере, именно поэтому я перечислил «cut [x]» задания, а не «wait to x», чтобы закипеть ».
Ваша работа (расчет) не асинхронный код. Он никогда не работает вхолостую (таким образом, что это ожидаемо), и поэтому он работает синхронно. Это означает, что вы не получаете никакой выгоды от выполнения этого асинхронно.
Сокращение вашего кода до более простого примера:
private static void CalculateStuff(Sample s, ProgressBar pb, ulong max)
{
Thread.Sleep(5000);
}
Проще говоря, это задание занимает 5 секунд и не может ждать . Если вы запустите 3 из этих задач одновременно, они все равно будут обрабатываться одна за другой, что займет 15 секунд .
Если работа внутри ваших задач действительно была ожидаемой, вы увидит выгоду времени. Например:
private static async void CalculateStuff(Sample s, ProgressBar pb, ulong max)
{
await Task.Delay(5000);
}
Это задание занимает 5 секунд , но может ожидаться . Если вы запустите 3 из этих задач одновременно, ваш поток не будет тратить время на холостом ходу (т. Е. Ожидает задержки) и вместо этого начнет выполнение следующей задачи. Поскольку он может ожидать (то есть ничего не делать) для этих задач одновременно, это означает, что общее время обработки занимает 5 секунд, общее (плюс незначительные накладные расходы).
Согласно окну отладки, загрузка ЦП увеличивается с увеличением числа задач.
Управление задачами требует небольших накладных расходов, что означает, что общий объем работы (который можно измерить в ЦП) использование во времени) немного выше по сравнению с синхронным кодом. Этого и следовало ожидать.
Эта небольшая стоимость обычно бледнеет по сравнению с преимуществами, полученными от хорошо написанного асинхронного кода. Однако ваш код просто не использует фактические выгоды от асинхронности, поэтому вы видите только накладные расходы, а не их преимущества, поэтому ваш мониторинг дает вам противоположный результат того, что вы ожидали.
- В моем компьютере 2 процессора по 10 ядер.
Ядра процессора, потоки и задачи - три совершенно разных зверя.
Задачи обрабатываются потоками, но они не обязательно имеют однозначное сопоставление. Возьмите пример команды из 4 разработчиков, которая должна решить 10 ошибок. Хотя это означает, что невозможно разрешить все 10 ошибок одновременно, эти разработчики (потоки) могут брать заявки (задачи) один за другим, принимая новый билет (задачу) всякий раз, когда они заканчивают свой предыдущий билет ( задача).
Ядра процессора похожи на рабочие станции. Нет смысла иметь меньше рабочих станций (процессорных ядер), чем у разработчиков (потоков), так как вы в конечном итоге будете работать с простыми разработчиками.
Кроме того, вы можете не захотеть, чтобы ваши разработчики могли претендовать на все рабочие станции. Возможно, персоналу отдела кадров и бухгалтерии (= другим процессам ОС) также необходимо иметь некоторые гарантированные рабочие станции, чтобы они могли выполнять свою работу.
Компания (= компьютер) не просто останавливается, потому что разработчики исправляют некоторые ошибки. Это то, что происходило на одноядерных машинах - если один процесс требует ЦП, больше ничего не может произойти. Если этот один процесс занимает много времени или зависает, все зависает.
Вот почему у нас есть пул потоков. Здесь нет прямой реальной аналогии (за исключением, может быть, консалтинговой фирмы, которая динамически регулирует, сколько разработчиков она отправляет в вашу компанию), но пул потоков в основном может решить, сколько разработчиков разрешено работать в компании на в то же время, чтобы гарантировать, что задачи разработки могут быть замечены как можно быстрее, а также чтобы другие отделы могли по-прежнему выполнять свою работу на рабочих станциях.
Это осторожный баланс, не посылая слишком много разработчиков, поскольку это затопляет системы, хотя и не посылают слишком мало разработчиков, так как это означает, что работа выполняется слишком медленно.
Точная конфигурация вашего пула потоков - это не то, что я могу устранить из-за простых вопросов и ответов. Но описанное вами поведение согласуется с меньшим количеством процессоров (выделенных для вашей среды выполнения) и / или потоков по сравнению с тем, сколько у вас задач.