При старте приложения надо запустить фоном выполнение нескольких тяжеловесных методов
Имеем:
var first = new Action(() => {});
var after1 = new Action(() => {});
var after2 = new Action(() => {});
...
var afterN = new Action(() => {});
Нужно дождаться выполнения метода first и запустить параллельно все остальные
Сейчас это работает так:
var _afters =
{
new Task(after1),
new Task(after2),
...
new Task(afterN),
}
var task = Task.Run(() => first).ContinueWith(t =>
{
_afters.ForEach(a => a.Start());
Task.WaitAll(_afters);
});
Можно ли написать как-то покрасивее?
Спасибо...
Re: Как правильно вызвать последовательность задач?
Здравствуйте, Nikolay_Ch, Вы писали: D>>Можно ли написать как-то покрасивее? N_C>А покрасивее — это как? Что смущает?
в 1 оператор
и не стартовать задачи из массива руками
в примерах вроде можно стартовать 1 задачу, остальные как-то подтягиваются
а у меня так не получается
Re[2]: Как правильно вызвать последовательность задач?
Здравствуйте, mDmitriy, Вы писали:
D>в 1 оператор
В один оператор — это все равно будет набор операторов, умещенных в одну строку (см. мой пример)
D>в примерах вроде можно стартовать 1 задачу, остальные как-то подтягиваются
В каких примерах? Покажите. Что значит "как-то"? Примеры надо разбирать, чтобы понимать это "как-то"
Re[3]: Как правильно вызвать последовательность задач?
Здравствуйте, karbofos42, Вы писали:
K>Вместо _afters завести ActionBlock?
Разные же анонимные методы для задач, а ActionBlock работает только с одним. Да и ActionBlock переходит в ошибку после первого эксепшина и не выполняет оставшуюся очередь — подойдет ли это топик стартеру.
... << RSDN@Home 1.3.108 alpha 5 rev. 56>>
Re[2]: Как правильно вызвать последовательность задач?
Здравствуйте, _Raz_, Вы писали:
_R_>Здравствуйте, karbofos42, Вы писали:
K>>Вместо _afters завести ActionBlock?
_R_>Разные же анонимные методы для задач, а ActionBlock работает только с одним. Да и ActionBlock переходит в ошибку после первого эксепшина и не выполняет оставшуюся очередь — подойдет ли это топик стартеру.
Ну, какую-нибудь такую штуку можно написать: ActionBlock<Action>((a) => a.Invoke()). Но для этого конечно Parallel.Invoke логичнее использовать.
Как я понял по описанию Parallel.Invoke, получится, что ContinueWith запускает Parallel.Invoke и ждет, пока он отработает(т.е. таска ContinueWith будет периодически получать время на выполнение, даже если не все задачи завершены).
Если же сделать как-нибудь через await
Здравствуйте, StatujaLeha, Вы писали:
SL>то таска ContinueWith запустит все задачи и в следующий раз будет разбужена только по завершении всех задач.
Э... Что? Первая таска завершится и после нее начнет выполнятся вторая таска. Вторая будет в состоянии ожидания, пока Parallel не завершится.
По-моему разницы быть не должно.
Здравствуйте, Nikolay_Ch, Вы писали:
N_C>Э... Что? Первая таска завершится и после нее начнет выполнятся вторая таска. Вторая будет в состоянии ожидания, пока Parallel не завершится. N_C>По-моему разницы быть не должно.
Есть тредпул. Потоки из него обрабатывают таски.
Идеальный вариант:
1. Приходим в таску ContinueWith и запускаем все нужные таски after.
2. Ставим на паузу таску ContinueWith.
3. Ждем, пока все таски after завершатся.
4. Идем обратно в таску ContinueWith и завершаем ее.
Т.е. мы тратим на таску ContinueWith ресурсы только тогда, когда оно реально необходимо.
В примере через await оно так и должно работать.
А вот в варианте через Parallel это мне непонятно...
Пришли мы к вызову Parallel.Invoke, запустили все таски after.
И как тредпул теперь поймет, что до завершения всех тасков after не надо тратить вычислительные ресурсы на таску, из которой запущен Parallel.Invoke?
Здравствуйте, StatujaLeha, Вы писали:
SL>И как тредпул теперь поймет, что до завершения всех тасков after не надо тратить вычислительные ресурсы на таску, из которой запущен Parallel.Invoke?
Посмотрите в исходники.Net... Там есть несколько вариантов работы Invoke, в зависимости от количества переданных Action'ов... Один из которых WaitAll, другой — FastWaitAll...
Здравствуйте, StatujaLeha, Вы писали:
SL>2. Ставим на паузу таску ContinueWith.
Это как?
SL>4. Идем обратно в таску ContinueWith и завершаем ее.
Откуда идем-то?
SL>Т.е. мы тратим на таску ContinueWith ресурсы только тогда, когда оно реально необходимо.
В рассматриваем примере этот таск завершится сразу после вызова WhenAll, то есть ресурсов на нее не будет затрачено совсем. Ресурсы будут использованы в таске, который создаст метод WhenAll.
SL>В примере через await оно так и должно работать.
Это потому что контекст захватили, а теперь добавь .ConfigureAwait(false) и посмотри что будет с таской ContinueWith.
SL>Пришли мы к вызову Parallel.Invoke, запустили все таски after.
Parallel.Invoke сам создает и запускает таски. Ему передаются экшены.
SL>И как тредпул теперь поймет, что до завершения всех тасков after не надо тратить вычислительные ресурсы на таску, из которой запущен Parallel.Invoke?
Почему не надо то, если там работает Parallel.Invoke.
Здравствуйте, Nikolay_Ch, Вы писали:
N_C>Здравствуйте, StatujaLeha, Вы писали:
SL>>И как тредпул теперь поймет, что до завершения всех тасков after не надо тратить вычислительные ресурсы на таску, из которой запущен Parallel.Invoke? N_C>Посмотрите в исходники.Net... Там есть несколько вариантов работы Invoke, в зависимости от количества переданных Action'ов... Один из которых WaitAll, другой — FastWaitAll...
Спасибо.
Т.е. там во всех ветках какой-либо Wait на другие таски.
Ждем на ManualResetEventSlim.
Тут мне и непонятно: если у меня есть Task и внутри него вызывается какой-то Task.Wait*, то как будет работать тредпул с этой таской?
Допустим, у нас первая таска завершается быстро, а остальные работают большее время.
В случае, ContinueWith(async (_) => await Task.WhenAll(...)) все равно будет один вход в таску с await и один выход из нее.
Я подозреваю, что в случае ContinueWith(() => Parallel.Invoke(...)) тред пул будет периодически давать время таске с Invoke и она будет просто тратить время на ожидание завершения остальных тасков, не выполняя полезной работы.
SL>В случае, ContinueWith(async (_) => await Task.WhenAll(...)) все равно будет один вход в таску с await и один выход из нее. SL>Я подозреваю, что в случае ContinueWith(() => Parallel.Invoke(...)) тред пул будет периодически давать время таске с Invoke и она будет просто тратить время на ожидание завершения остальных тасков, не выполняя полезной работы.
Ну так посмотрите в исходниках в чем отличие WhenAll от WaitAll и FastWaitAll
Думаю, что Вы удивитесь (а может и нет — смотря с какой стороны посмотреть)
Здравствуйте, _Raz_, Вы писали:
SL>>2. Ставим на паузу таску ContinueWith. _R_>Это как?
Через await. Тред пул не будет передавать таску потоку из пула, пока не завершится таска под await-ом.
SL>>4. Идем обратно в таску ContinueWith и завершаем ее. _R_>Откуда идем-то?
Тредпул мониторит, что завершилась таска под await-ом и это сигнал, что можно продолжить таску, которая содержит этот await.
SL>>Т.е. мы тратим на таску ContinueWith ресурсы только тогда, когда оно реально необходимо. _R_>В рассматриваем примере этот таск завершится сразу после вызова WhenAll, то есть ресурсов на нее не будет затрачено совсем. Ресурсы будут использованы в таске, который создаст метод WhenAll.
Сценарий: первая таска из after выполняется очень быстро, оставшиеся медленно.
Получится, что Parallel.Invoke будет тратить ресурсы на то, чтобы периодически чекать, завершились ли задачи из after.
А этого можно избежать.
SL>>И как тредпул теперь поймет, что до завершения всех тасков after не надо тратить вычислительные ресурсы на таску, из которой запущен Parallel.Invoke? _R_>Почему не надо то, если там работает Parallel.Invoke.
А смысл?
Туда нужно вернуться один раз, когда все задачи завершены.
В примере через await так и произойдет.
Здравствуйте, StatujaLeha, Вы писали:
SL>Сценарий: первая таска из after выполняется очень быстро, оставшиеся медленно. SL>Получится, что Parallel.Invoke будет тратить ресурсы на то, чтобы периодически чекать, завершились ли задачи из after.
Это откуда такое получается? В Parallel.Invoke нет никакого периодического чеканья.
SL>А смысл?
Не забывай, что в Parallel.Invoke передаются экшены и фабрика тасков для этих экшенов должна где-то выполняться.
SL>Туда нужно вернуться один раз, когда все задачи завершены.
А Parallel.Invoke никуда и не уходил, а это менее затратно по ресурсам
SL>В примере через await так и произойдет.
А дальше что? А дальше сразу конец метода. Вот и получилось, что держали-держали поток и сразу вышли Зачем держали
Здравствуйте, _Raz_, Вы писали:
_R_>Это откуда такое получается? В Parallel.Invoke нет никакого периодического чеканья.
Есть. Если чекнуть вторую ссылку на исходники фреймворка, которую я дал в сообщении ранее, то там будет функция WaitAllBlockingCore.
Parallel.Invoke может по разному запустить переданные ему Action-ы, но в конце всегда будет какой-либо Wait.
Изучив Wait-ы, можно понять, что ожидание будет выполняться через ManualResetEventSlim. https://msdn.microsoft.com/en-us/library/system.threading.manualreseteventslim(v=vs.110).aspx
ManualResetEventSlim uses busy spinning for a short time while it waits for the event to become signaled.
_R_>Не забывай, что в Parallel.Invoke передаются экшены и фабрика тасков для этих экшенов должна где-то выполняться.
Так и в чем проблема?
Например, если экшенов относительно мало, то все выполняется следующим кодом из потока, запустившего Invoke
// Launch all actions as tasksfor (int i = 1; i < tasks.Length; i++)
{
tasks[i] = Task.Factory.StartNew(actionsCopy[i], parallelOptions.CancellationToken, TaskCreationOptions.None,
InternalTaskOptions.None, parallelOptions.EffectiveTaskScheduler);
}
_R_>А Parallel.Invoke никуда и не уходил, а это менее затратно по ресурсам
Как бы в этом и нюанс, что в некоторых сценариях таска с Parallel.Invoke будет кушать вычислительные ресурсы, не направленные на выполнение задач after.
Так что это точно не менее затратно по ресурсам.
_R_>А дальше что? А дальше сразу конец метода. Вот и получилось, что держали-держали поток и сразу вышли Зачем держали
Потоками тредпул рулит. А не таски.
И выделенное сообщение: суть await-а в том, что при его достижении тредпула отберет поток у задачи и не будет назначать до тех пор, пока таска под await-ом не завершится.
Здравствуйте, StatujaLeha, Вы писали:
SL>Как бы в этом и нюанс, что в некоторых сценариях таска с Parallel.Invoke будет кушать вычислительные ресурсы, не направленные на выполнение задач after.
Погодите-ка... Но WhenAll работает так же, как и Parallell.Invoke. Где будет экономия?
Здравствуйте, Nikolay_Ch, Вы писали:
N_C>Ну так посмотрите в исходниках в чем отличие WhenAll от WaitAll и FastWaitAll N_C>Думаю, что Вы удивитесь (а может и нет — смотря с какой стороны посмотреть)
Да, посмотрел.
WhenAll сам по себе не делает busy waiting, а WaitAll и FastWaitAll делают.
Выходит, что я убираю один busy waiting, если делаю через await.
Или я что-то не учел?
Здравствуйте, StatujaLeha, Вы писали:
SL>WhenAll сам по себе не делает busy waiting, а WaitAll и FastWaitAll делают.
Не делает, но (судя по исходникам) и параллельно задачи не запускает, т.е. выполняет их последовательно одну за одной...
Здравствуйте, StatujaLeha, Вы писали:
SL>Есть. Если чекнуть вторую ссылку на исходники фреймворка, которую я дал в сообщении ранее, то там будет функция WaitAllBlockingCore. SL>Parallel.Invoke может по разному запустить переданные ему Action-ы, но в конце всегда будет какой-либо Wait. SL>Изучив Wait-ы, можно понять, что ожидание будет выполняться через ManualResetEventSlim. SL>https://msdn.microsoft.com/en-us/library/system.threading.manualreseteventslim(v=vs.110).aspx SL>
SL>ManualResetEventSlim uses busy spinning for a short time while it waits for the event to become signaled.
Про чеканье стало понятно. Теперь возник вопрос в чем разница Parallel.Invoke и Task.WaitAll в разрезе этого чеканья.
_R_>>Не забывай, что в Parallel.Invoke передаются экшены и фабрика тасков для этих экшенов должна где-то выполняться. SL>Так и в чем проблема?
Так и нет проблемы. Это ответ на "А смысл? Туда нужно вернуться один раз, когда все задачи завершены.".
SL>Например, если экшенов относительно мало, то все выполняется следующим кодом из потока, запустившего Invoke
Если ты видишь и понимаешь этот код, то смысл чего тебе не понятен?
SL>Как бы в этом и нюанс, что в некоторых сценариях таска с Parallel.Invoke будет кушать вычислительные ресурсы, не направленные на выполнение задач after.
А что так загадочно? Рассказывай про сценарии и озвучь список ресурсов.
SL>Так что это точно не менее затратно по ресурсам.
Это "точно" учитывает все if-ы внутри Parallel.Invoke и учитывает отсутствие переключение контекстов и дороговизну создания новых потоков (это про MaxDegreeOfParallelism)?
_R_>>А дальше что? А дальше сразу конец метода. Вот и получилось, что держали-держали поток и сразу вышли Зачем держали SL>Потоками тредпул рулит. А не таски.
И что? Поток то все равно есть и он занимает ресурсы (не направленные на выполнение задач after ).
SL>И выделенное сообщение: суть await-а в том, что при его достижении тредпула отберет поток у задачи и не будет назначать до тех пор, пока таска под await-ом не завершится.
Суть await-а в синтаксическом сахаре. Если переписать на продолжениях суть изменится или нет?
И можно про то как тредпул отбирает поток у задачи?
Здравствуйте, Nikolay_Ch, Вы писали:
N_C>Не делает, но (судя по исходникам) и параллельно задачи не запускает, т.е. выполняет их последовательно одну за одной...
Он их вообще не запускает.
Поэтому в моем примере туда передаются задачи из Task.Run, т.е. уже запущенные(это обеспечит параллельность).
Здравствуйте, StatujaLeha, Вы писали:
N_C>>Не делает, но (судя по исходникам) и параллельно задачи не запускает, т.е. выполняет их последовательно одну за одной... SL>Он их вообще не запускает.
Согласен, недосмотрел.
SL>Поэтому в моем примере туда передаются задачи из Task.Run, т.е. уже запущенные(это обеспечит параллельность). SL>
Здравствуйте, _Raz_, Вы писали:
_R_>Про чеканье стало понятно. Теперь возник вопрос в чем разница Parallel.Invoke и Task.WaitAll в разрезе этого чеканья.
Ок.
Между Parallel.Invoke и Task.WaitAll нет разницы.
А между Parallel.Invoke и Task.When All она есть.
_R_>Так и нет проблемы. Это ответ на "А смысл? Туда нужно вернуться один раз, когда все задачи завершены.".
Так и я думаю, что проблемы нет. Просто я не понял, к чему замечание "Не забывай, что в Parallel.Invoke передаются экшены и фабрика тасков для этих экшенов должна где-то выполняться."?
Это же ни на что не влияет.
_R_>Если ты видишь и понимаешь этот код, то смысл чего тебе не понятен?
Мне как раз все понятно
_R_>А что так загадочно? Рассказывай про сценарии и озвучь список ресурсов.
_R_>Это "точно" учитывает все if-ы внутри Parallel.Invoke и учитывает отсутствие переключение контекстов и дороговизну создания новых потоков (это про MaxDegreeOfParallelism)?
Ты о чем? В примере топикстартера Parallel.Invoke точно так же скидывает таски в тред пул, в котором все потоки уже созданы.
Таски в обоих вариантах запускаются идентично.
_R_>И что? Поток то все равно есть и он занимает ресурсы (не направленные на выполнение задач after ).
Это не так. Видимо, надо изучить матчасть.
_R_>Суть await-а в синтаксическом сахаре. Если переписать на продолжениях суть изменится или нет? _R_>И можно про то как тредпул отбирает поток у задачи?
Ясно
Рекомендую изучить матчать: async/await вообще-то заставляют компилятор трансформировать функцию.
Google в помощь: "async await state machine".
Не то чтобы чтиво большое, но и не пару строк.
Здравствуйте, Nikolay_Ch, Вы писали:
N_C>Ну, тогда у меня последний аргумент — Parallel может лучше суметь распараллелить таски, чем последовательный их запуск...
В смысле?
При дефолтных аргументах(как у автора) что в моем примере, что внутри у Parallel.Invoke таски будут идентично запущены.
PS Task.Run() не дожидается окончания работы таски, которую он создал.
Здравствуйте, StatujaLeha, Вы писали:
SL>Ок. SL>Между Parallel.Invoke и Task.WaitAll нет разницы. SL>А между Parallel.Invoke и Task.When All она есть.
Ок.
SL>Так и я думаю, что проблемы нет. Просто я не понял, к чему замечание "Не забывай, что в Parallel.Invoke передаются экшены и фабрика тасков для этих экшенов должна где-то выполняться."? SL>Это же ни на что не влияет.
Это влияет на то, почему Parallel.Invoke работает в потоке ContinueWith — в этом же твое изначальное замечание
было. А теперь, оказывается, что ни на что не влияет
_R_>>А что так загадочно? Рассказывай про сценарии и озвучь список ресурсов. SL>Я его уже озвучивал тебе: https://rsdn.org/forum/dotnet/6904266.1
Это вот этот: "Сценарий: первая таска из after выполняется очень быстро, оставшиеся медленно."?
И из него ты делаешь вывод: "Как бы в этом и нюанс, что в некоторых сценариях таска с Parallel.Invoke будет кушать вычислительные ресурсы, не направленные на выполнение задач after."?
SL>Ты о чем?
Я о категоричности "это точно не менее затратно по ресурсам".
SL>В примере топикстартера Parallel.Invoke точно так же скидывает таски в тред пул, в котором все потоки уже созданы. SL>Таски в обоих вариантах запускаются идентично.
Значит ты смотрел исходники Parallel.Invoke, сказал, что понял их, и все равно настаиваешь на категоричном "идентично"?
_R_>>И что? Поток то все равно есть и он занимает ресурсы (не направленные на выполнение задач after ). SL>Это не так. Видимо, надо изучить матчасть.
Значит, все-таки настаиваешь, что "тредпул отберет поток"?
SL>Ясно SL>Рекомендую изучить матчать: async/await вообще-то заставляют компилятор трансформировать функцию. SL>Google в помощь: "async await state machine". SL>Не то чтобы чтиво большое, но и не пару строк.
Воспользовался твоими рекомендациями, но подтверждения для "суть await-а в том, что при его достижении тредпула отберет поток у задачи и не будет назначать до тех пор, пока таска под await-ом не завершится" не нашел. Так что позволю себе повторить вопросы:
Если await переписать на продолжениях суть изменится или нет?
И можно про то как тредпул отбирает поток у задачи?
Здравствуйте, StatujaLeha, Вы писали:
SL>Здравствуйте, Nikolay_Ch, Вы писали: SL>В смысле? SL>При дефолтных аргументах(как у автора) что в моем примере, что внутри у Parallel.Invoke таски будут идентично запущены.
Вот из документации:
// In the algorithm below, if the number of actions is greater than this, we automatically
// use Parallel.For() to handle the actions, rather than the Task-per-Action strategy.
В общем, все Экшны стартуют как Таски только для количества меньше 10...
Здравствуйте, Nikolay_Ch, Вы писали:
N_C>Вот из документации: N_C>
N_C>// In the algorithm below, if the number of actions is greater than this, we automatically
N_C>// use Parallel.For() to handle the actions, rather than the Task-per-Action strategy.
N_C>В общем, все Экшны стартуют как Таски только для количества меньше 10...
было. А теперь, оказывается, что ни на что не влияет
Чего?
Мое изначальное замечание действительно в этом сообщении. Как мне кажется, оно ясно сформулировано
Как я понял по описанию Parallel.Invoke, получится, что ContinueWith запускает Parallel.Invoke и ждет, пока он отработает(т.е. таска ContinueWith будет периодически получать время на выполнение, даже если не все задачи завершены).
И мой вариант убирает это ожидание.
_R_>>>А что так загадочно? Рассказывай про сценарии и озвучь список ресурсов. SL>>Я его уже озвучивал тебе: https://rsdn.org/forum/dotnet/6904266.1
_R_>Это вот этот: "Сценарий: первая таска из after выполняется очень быстро, оставшиеся медленно."? _R_>И из него ты делаешь вывод: "Как бы в этом и нюанс, что в некоторых сценариях таска с Parallel.Invoke будет кушать вычислительные ресурсы, не направленные на выполнение задач after."?
Да, я так думаю. Если первая таска выполнится быстро, то пойдет ожидание на ManualResetEventSlim, busy waiting.
SL>>Ты о чем? _R_>Я о категоричности "это точно не менее затратно по ресурсам".
Ну ок. У меня пока замеров нету, так что снимаю это утверждение.
Тогда попрошу как-то подкрепить утверждение отсюда: http://rsdn.org/forum/dotnet/6904317.1
А Parallel.Invoke никуда и не уходил, а это менее затратно по ресурсам
Как бы выходит, что в приведенном мной сценарии Parallel.Invoke действительно никуда не уходил и кушал ресурсы на ManualResetEventSlim.
Почему же это считается менее затратным?
_R_>Значит ты смотрел исходники Parallel.Invoke, сказал, что понял их, и все равно настаиваешь на категоричном "идентично"?
Согласен, слово "идентично" не подходит.
Если нет возражений, то заменю на "схожим образом".
Мой запуск
// Launch all actions as tasksfor (int i = 1; i < tasks.Length; i++)
{
tasks[i] = Task.Factory.StartNew(actionsCopy[i], parallelOptions.CancellationToken, TaskCreationOptions.None,
InternalTaskOptions.None, parallelOptions.EffectiveTaskScheduler);
}
// Optimization: Use current thread to run something before we block waiting for all tasks. tasks[0] = new Task(actionsCopy[0]);
tasks[0].RunSynchronously(parallelOptions.EffectiveTaskScheduler);
Разница в выделенном.
_R_>Значит, все-таки настаиваешь, что "тредпул отберет поток"?
https://www.codeproject.com/Articles/535635/Async-Await-and-the-Generated-StateMachine
Можешь сам убедиться: в состояниях State Machine, соответствующих await-ам, делается запуск операции под await-ом и дальше выход из функции MoveNext.
После этого поток свободен.
_R_>Воспользовался твоими рекомендациями, но подтверждения для "суть await-а в том, что при его достижении тредпула отберет поток у задачи и не будет назначать до тех пор, пока таска под await-ом не завершится" не нашел. Так что позволю себе повторить вопросы:
Ок
Как объяснишь результаты работы кода ниже?
Особенно выделенной части, когда исходный Action до await-а обрабатывается тредом 4, после await-а тредом 3, а Continue снова тредом 4.
_R_>Если await переписать на продолжениях суть изменится или нет?
Кинь плз пример кода, как ты предлагаешь это сделать.
_R_>И можно про то как тредпул отбирает поток у задачи?
Я механику в деталях не знаю, но выше привел пример, где зафиксировано такое поведение + ссылку на декомпилированную state machine, в которой тоже видно, что после await-а поток освобождается.
Того
SL>Мое изначальное замечание действительно в этом сообщении. Как мне кажется, оно ясно сформулировано SL>
SL>Как я понял по описанию Parallel.Invoke, получится, что ContinueWith запускает Parallel.Invoke и ждет, пока он отработает(т.е. таска ContinueWith будет периодически получать время на выполнение, даже если не все задачи завершены).
А я тебе на это отвечаю — "Не забывай, что в Parallel.Invoke передаются экшены и фабрика тасков для этих экшенов должна где-то выполняться."
То есть он не ждет, а выполняет полезную работу. Так понятней, надеюсь
SL>И мой вариант убирает это ожидание.
Я отвечал на то что до скобок.
SL>Да, я так думаю. Если первая таска выполнится быстро, то пойдет ожидание на ManualResetEventSlim, busy waiting.
Так оно в любом случае будет. Не обязательно busy, но будет.
SL>Как бы выходит, что в приведенном мной сценарии Parallel.Invoke действительно никуда не уходил и кушал ресурсы на ManualResetEventSlim. SL>Почему же это считается менее затратным?
И в выделенном, и до выделенного, и после выделенного. Я все пытаюсь тебя подтолкнуть, что Parallel.Invoke это чуть больше запуска потоков — там и некоторая оптимизация и сбор ошибок.
SL>Можешь сам убедиться: в состояниях State Machine, соответствующих await-ам, делается запуск операции под await-ом и дальше выход из функции MoveNext.
Я спрашивал про тредпул. Как называется его метод отбирающий поток?
SL>Как объяснишь результаты работы кода ниже? SL>Особенно выделенной части, когда исходный Action до await-а обрабатывается тредом 4, после await-а тредом 3, а Continue снова тредом 4.
Хватит уже щеки надувать. Ты меня не собеседуешь.
SL>Кинь плз пример кода, как ты предлагаешь это сделать.
Вот сейчас было ваще на отличненько
SL> выше привел пример, где зафиксировано такое поведение + ссылку на декомпилированную state machine, в которой тоже видно, что после await-а поток освобождается.
Значит даю направление: тредпул во всей этой истории не играет ни малейшей роли и ничего ни у кого не отбирает. Так же как и await — да, да, он все еще синтаксический сахар и не важно во что он там разворачивается (кстати, ты и тут ошибаешься: разворачивается async, а не await). А играют тут шедулер и контекст выполнения.
Здравствуйте, _Raz_, Вы писали:
_R_>То есть он не ждет, а выполняет полезную работу. Так понятней, надеюсь
_R_>Я отвечал на то что до скобок.
Полезная работа Parallel.Invoke в целом такая же, как и полезная работа Task.WhenAll.
А вот busy waiting, который может произойти в Parallel.Invoke, можно избежать.
_R_>Так оно в любом случае будет. Не обязательно busy, но будет.
Разница в том, что на этот busy waiting из Parallel.Invoke будут тратится ресурсы потока из тред пула.
Хотя их можно потратить на другую работу.
_R_>Ты там смеющийся смайлик не заметил?
Ах, смайлик!
_R_>И в выделенном, и до выделенного, и после выделенного. Я все пытаюсь тебя подтолкнуть, что Parallel.Invoke это чуть больше запуска потоков — там и некоторая оптимизация и сбор ошибок.
Если можно, не подталкивай, а просто покажи
До сих пор ты только пишешь и ничем свои доводы не обосновываешь.
При этом пример кода, когда твои слова явно не соответствуют действительности, уже был приведен.
_R_>Я спрашивал про тредпул. Как называется его метод отбирающий поток?
Ок, признаю: тред пул не отбирает поток.
Поток тредпула, выделенный на задачу, сам туда возвращается
Как все поменялось!
Вот и получилось, что держали-держали поток и сразу вышли
Хотя в предыдущем сообщении была ссылка на листинг, из которого явно видно, что поток нифига не держится таской.
Комментарии будут?
_R_>Хватит уже щеки надувать. Ты меня не собеседуешь.
Ой все?
Как-то так выходит, что ты говорил одно, а работает оно иначе
Не смущает?
_R_>
...
_R_>
Авторского примера плз, его же обсуждаем.
_R_>Вот сейчас было ваще на отличненько
Да ладно. Не сравнится, с твоим "хватит щеки надувать" вместо ответа по существу :D
_R_>Значит даю направление: тредпул во всей этой истории не играет ни малейшей роли и ничего ни у кого не отбирает. Так же как и await — да, да, он все еще синтаксический сахар и не важно во что он там разворачивается
Ок, пусть async/await будет считать синтаксическим сахаром.
Но в свою очередь тоже даю тебе направление: иди учи матчасть.
_R_>(кстати, ты и тут ошибаешься: разворачивается async, а не await).
И не перевирай мои слова: я писал "async/await вообще-то заставляют компилятор трансформировать функцию".
А матчасть подучи. Тогда будешь понимать, что async — это флажок компилятору, что функцию надо трансформировать в State Machine, а количество состояний в этой State Machine зависит от количества await-ов.
_R_>А играют тут шедулер и контекст выполнения.
Вот ты постоянной что-то пишешь и ничем свои доводы не подкрепляешь.
А уже ведь было выяснено, что твои доводы бывают явно не верными.
Как бы буду рад, если кто-то покажет мне, в чем я заблуждаюсь/не прав.
Код так не тривиальный, может я что-то и упустил.
Но явно я не буду как-то всерьез воспринимать доводы от авторов, которые тупо в матчасти плавают
Здравствуйте, StatujaLeha, Вы писали:
SL>Полезная работа Parallel.Invoke в целом такая же, как и полезная работа Task.WhenAll.
Ты издеваешься?
SL>А вот busy waiting, который может произойти в Parallel.Invoke, можно избежать. SL>Разница в том, что на этот busy waiting из Parallel.Invoke будут тратится ресурсы потока из тред пула.
Ты точно издеваешься. В первом предложении это предположение, а во втором уже утверждение.
SL>Хотя их можно потратить на другую работу.
Ну ты же понимаешь насколько это ерунда.
SL>Если можно, не подталкивай, а просто покажи
Ты же видел исходник Parallel.Invoke полностью и сказал, что понял его. Если ты его понял, то что тебе еще показать? Разницу между одной строкой и 197-ю строками?
SL>До сих пор ты только пишешь и ничем свои доводы не обосновываешь. SL>При этом пример кода, когда твои слова явно не соответствуют действительности, уже был приведен.
Для меня очевидно, что ты мухлюешь и передергиваешь. И твои голословные обвинения идут лесом. И тот кусок был приведен тобой для подтверждения твоей же позиции — как тут я могу быть не прав? К тому-же позициции, которую ты поменял в двух последовательных сообщениях.
SL>Ок, признаю: тред пул не отбирает поток. SL>Поток тредпула, выделенный на задачу, сам туда возвращается
Это же твоя базовая аргументация:
Потоками тредпул рулит. А не таски.
И выделенное сообщение: суть await-а в том, что при его достижении тредпула отберет поток у задачи и не будет назначать до тех пор, пока таска под await-ом не завершится.
Хотя, да — в главном то ты прав
SL>Как все поменялось!
SL>Вот и получилось, что держали-держали поток и сразу вышли
SL>Хотя в предыдущем сообщении была ссылка на листинг, из которого явно видно, что поток нифига не держится таской.
В собственном глазу соринку не замечаешь? "и в следующий раз будет разбужена только по завершении всех задач.".
SL>Как-то так выходит, что ты говорил одно, а работает оно иначе SL>Не смущает?
Не смеши меня.
SL>Авторского примера плз, его же обсуждаем.
В авторском коде нет ни одного await-а.
_R_>>(кстати, ты и тут ошибаешься: разворачивается async, а не await). SL>И не перевирай мои слова: я писал "async/await вообще-то заставляют компилятор трансформировать функцию".
Я и не перевираю. И ниже ты сам подтверждаешь мои слова — await не заставляет трансформировать функцию.
SL>А матчасть подучи. Тогда будешь понимать, что async — это флажок компилятору, что функцию надо трансформировать в State Machine, а количество состояний в этой State Machine зависит от количества await-ов.
Оставь менторский тон для зеркала.
SL>Вот ты постоянной что-то пишешь и ничем свои доводы не подкрепляешь. SL>А уже ведь было выяснено, что твои доводы бывают явно не верными.
Вот нравится мне твоя категоричность. Это юношеский максимализм, похоже.
SL>Как бы буду рад, если кто-то покажет мне, в чем я заблуждаюсь/не прав. SL>Код так не тривиальный, может я что-то и упустил.
Ты главное упустил — никому не интересно сколько ресурсов занимает ожидание.
Здравствуйте, _Raz_, Вы писали:
_R_>Ты издеваешься?
Так аргументируй, если не согласен.
Показал бы, где я не прав
_R_>Ты точно издеваешься. В первом предложении это предположение, а во втором уже утверждение.
Нет. Просто ты уже за отсутствием аргументов цепляешься к словам
А сценарий, где это busy waiting возникает, выше был приведен. Для него это утверждение справедливо.
_R_>Ну ты же понимаешь насколько это ерунда.
Если ты имеешь ввиду, что выигрыш от исключения busy waiting в приведенном мной сценарии мизерный, то, может и так.
У меня замеров нет, но допускаю, что мы говорим о каких-то процентах.
_R_>Ты же видел исходник Parallel.Invoke полностью и сказал, что понял его. Если ты его понял, то что тебе еще показать? Разницу между одной строкой и 197-ю строками?
Понимаешь, я посмотрел исходники Parallel.Invoke и там 198 строк. И это учитывая заглавие функции, комментарии, пустые строки, строки, где какая-нибудь фигня написана, типа одной скобки, логирование.
Строк, реализующих логику, в лучшем случае половина. Как видишь, строк только формально 198
А еще, я посмотрел исходники Task.WhenAll.
И ты удивишься, но там в сумме выходит 124 строки, при чем комментов и прочего барахла поменьше
Не удивлюсь, если при аккуратном подсчете содержательных строк выйдет, что кода примерно одинаково.
_R_>Для меня очевидно, что ты мухлюешь и передергиваешь.
Really?
А я думаю, что мухлеж и передергивание — это вот только что выше от тебя было, при сравнении размера кода Paralle.Invoke и Task.WhenAll
Ты при этом еще и выделил слово "полностью"
_R_>И твои голословные обвинения идут лесом.
Код, который явно работает не так, как ты писал, я не убирал и не подправлял
_R_>И тот кусок был приведен тобой для подтверждения твоей же позиции — как тут я могу быть не прав?
Ну и кто тут передергивает и мухлюет?
Ты не согласился с моим утверждением и сделал свое(ссылку на него я привел).
Я признал, что в моем утверждении есть неточности и поправил их.
И привел пример того, как твои слова полностью противоречат тому, как работает.
_R_>К тому-же позициции, которую ты поменял в двух последовательных сообщениях.
Моя изначальная позиция была не полностью корректна, поэтому я ее поменял и уведомил тебя об этом явно.
В то же время, твоя позиция до сих пор как была некорректной, так и осталась.
Просто ты этого не признаешь и все.
_R_>Это же твоя базовая аргументация: _R_>
_R_>Потоками тредпул рулит. А не таски.
_R_>И выделенное сообщение: суть await-а в том, что при его достижении тредпула отберет поток у задачи и не будет назначать до тех пор, пока таска под await-ом не завершится.
_R_>Хотя, да — в главном то ты прав
Ну как базовая? Она была исходной, но я признал, что там есть неточности, и поправил свою позицию.
Я помню это, спасибо
SL>>Как все поменялось!
В контексте вопроса, из которого зародился этот спор, не поменялось ничего.
_R_>"и в следующий раз будет разбужена только по завершении всех задач.".
Это утверждение я пока не снимал
Если считаешь, что оно не справедливо, то вперед, буду рад, если опровергнешь
_R_>Не смеши меня.
Да это не смешно...
_R_>В авторском коде нет ни одного await-а.
_R_>Я и не перевираю. И ниже ты сам подтверждаешь мои слова — await не заставляет трансформировать функцию.
Я вижу, что ты очень креативно повыдергивал слова из моего утверждения. У меня была пара async/await, ты оставил только async. И вуаля, "он не прав"!
_R_>Оставь менторский тон для зеркала.
Нет смысла, я и так постоянно что-то изучаю.
_R_>Вот нравится мне твоя категоричность. Это юношеский максимализм, похоже.
А, т.е. если тебя просят подкрепить свои слова чем-то, то это сразу юношеский максимализм?
Класс, чо.
_R_>Ты главное упустил — никому не интересно сколько ресурсов занимает ожидание.
Дак я согласен, что наша дискуссия может оказаться бесплодной. Какие-нибудь 1-2% по времени.
Ранее ты писал, что "играют тут шедулер и контекст выполнения".
Прояснишь ситуацию? Или "маэстро" не видит в этом необходимости?
Здравствуйте, StatujaLeha, Вы писали:
SL>Так аргументируй, если не согласен. SL>Показал бы, где я не прав
Так ты выдвигаешь голословные утверждения. Я не собираюсь искать аргументы против категоричного и для меня очевидно ошибочного "Полезная работа Parallel.Invoke в целом такая же, как и полезная работа Task.WhenAll.".
_R_>>Ты точно издеваешься. В первом предложении это предположение, а во втором уже утверждение. SL>Нет. Просто ты уже за отсутствием аргументов цепляешься к словам
Аргументируй, если не согласен.
_R_>>Ну ты же понимаешь насколько это ерунда. SL>Если ты имеешь ввиду, что выигрыш от исключения busy waiting в приведенном мной сценарии мизерный, то, может и так. SL>У меня замеров нет, но допускаю, что мы говорим о каких-то процентах.
Это не мы говорим, это ты говоришь.
SL>Понимаешь, я посмотрел исходники Parallel.Invoke и там 198 строк. И это учитывая заглавие функции, комментарии, пустые строки, строки, где какая-нибудь фигня написана, типа одной скобки, логирование. SL>А еще, я посмотрел исходники Task.WhenAll.
Опять мухлюешь — здесь речь идет о сравнении с "await Task.WhenAll(Task.Run(after1), Task.Run(after2), Task.Run(after3))"
_R_>>Для меня очевидно, что ты мухлюешь и передергиваешь. SL>Really?
Да. В каждом посте.
SL>А я думаю, что мухлеж и передергивание — это вот только что выше от тебя было, при сравнении размера кода Paralle.Invoke и Task.WhenAll SL>Ты при этом еще и выделил слово "полностью"
Да, потому что без подтасовок сравнение было с одной строкой.
_R_>>И твои голословные обвинения идут лесом. SL>Код, который явно работает не так, как ты писал, я не убирал и не подправлял
То что ты не убирал и не подправлял не является аргументом твоей правоты.
SL>Ну и кто тут передергивает и мухлюет? SL>Ты не согласился с моим утверждением и сделал свое(ссылку на него я привел). SL>Я признал, что в моем утверждении есть неточности и поправил их. SL>И привел пример того, как твои слова полностью противоречат тому, как работает.
Понимаешь, все это — исключительно в твоей вселенной.
SL>В то же время, твоя позиция до сих пор как была некорректной, так и осталась. SL>Просто ты этого не признаешь и все.
Просто ты не можешь сформулировать свои мысли. Все что я видел — это голословные обвинения.
SL>Ну как базовая? Она была исходной, но я признал, что там есть неточности, и поправил свою позицию. SL>В контексте вопроса, из которого зародился этот спор, не поменялось ничего.
Это называется юлить.
_R_>>"и в следующий раз будет разбужена только по завершении всех задач.". SL>Это утверждение я пока не снимал SL>Если считаешь, что оно не справедливо, то вперед, буду рад, если опровергнешь
Не хочу идти на новую итерацию сего интереснейшего кхм... разговора.
_R_>>В авторском коде нет ни одного await-а. SL>Если про мой вариант, то тогда этого:
Не-а. Не буду код писать. Догадаешься почему? Подсказка — дело не во мне.
SL>Я вижу, что ты очень креативно повыдергивал слова из моего утверждения. У меня была пара async/await, ты оставил только async. И вуаля, "он не прав"!
Именно так — только async. И, представь себе, что бы оставить только async — это слово надо выдернуть.
SL>Ранее ты писал, что "играют тут шедулер и контекст выполнения". SL>Прояснишь ситуацию? Или "маэстро" не видит в этом необходимости?
Необходимости и раньше не было, а вот готовность поговорить об этом была. Но не в подобном русле.
Здравствуйте, _Raz_, Вы писали:
_R_>Так ты выдвигаешь голословные утверждения. Я не собираюсь искать аргументы против категоричного и для меня очевидно ошибочного "Полезная работа Parallel.Invoke в целом такая же, как и полезная работа Task.WhenAll.".
Настолько "очевидного", что даже не можешь в открытых листингах фреймворка показать, в чем же я не прав
_R_>Опять мухлюешь — здесь речь идет о сравнении с "await Task.WhenAll(Task.Run(after1), Task.Run(after2), Task.Run(after3))"
Любому первокласснику очевидно, что это в обоих примерах по одной строке кода.
Ну так казалось, до твоего "сравнения"...
Мы же не знали, что надо посмотреть, сколько строк занимает Parallel.Invoke, при этом не смотря на размер Task.WhenAll..
Только тогда станет очевидно, что второй пример в 198 раз больше!
Спасибо, что просвятил!
_R_>>>Для меня очевидно, что ты мухлюешь и передергиваешь. SL>>Really?
_R_>Да. В каждом посте.
Ясно, понятно. Я не терапевт, дальше бессилен.
В дальнейшей беседе смысла не вижу: в мой адрес уже только и летят фразы "голословные обвинения", а вот с подкреплением делом этих слов пока движа нет
Свои аргументы я изложил, сообщения не редактировал, все на месте. Исходники фреймворка тоже открыты.
Если кому интересно, то проблем чекнуть справедливость моих слов нет. Ну или опровергнуть их
Удачи!
Здравствуйте, StatujaLeha, Вы писали:
_R_>>Так ты выдвигаешь голословные утверждения. Я не собираюсь искать аргументы против категоричного и для меня очевидно ошибочного "Полезная работа Parallel.Invoke в целом такая же, как и полезная работа Task.WhenAll.". SL>Настолько "очевидного", что даже не можешь в открытых листингах фреймворка показать, в чем же я не прав
Очевидно, что исходники фреймворка не могут являтся ни аргументами, ни показателем интенсивности моего чувства очевидности — уже передергиваешь. И тут же юлишь: я выдвинул утверждение о том, что твое заявление голословно, ты же, вместо аргументации в свою пользу, обвиняешь меня в том, что я не могу указать на твою не правоту.
SL>Есть код SL>
Все еще их придерживаешься?
SL>Любому первокласснику очевидно, что это в обоих примерах по одной строке кода.
Не любому, а умеющему юлить и передергивать.
SL>Ну так казалось, до твоего "сравнения"...
Не до моего, а до твоего. Ты не сравнивал количество строк, но ты сравнивал одну строку с WhenAll и кусок фреймворка.
SL>Мы же не знали, что надо посмотреть, сколько строк занимает Parallel.Invoke, при этом не смотря на размер Task.WhenAll..
Конечно, потому что смотрели на вызов из одной строки и всю функцию.
SL>Только тогда станет очевидно, что второй пример в 198 раз больше!
Уже хорошо. А теперь расскажи как разный код, разного размера, обладающий разной функциональность может иметь в целом одинаковую полезную работу.
SL>Ясно, понятно. Я не терапевт, дальше бессилен. SL>В дальнейшей беседе смысла не вижу: в мой адрес уже только и летят фразы "голословные обвинения", а вот с подкреплением делом этих слов пока движа нет