Re[7]: Так подойдет, спасибо!
От: _Raz_  
Дата: 14.09.17 13:58
Оценка:
Здравствуйте, 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.
... << RSDN@Home 1.3.108 alpha 5 rev. 56>>
Re[8]: Так подойдет, спасибо!
От: StatujaLeha на правах ИМХО
Дата: 14.09.17 14:47
Оценка:
Здравствуйте, 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 и она будет просто тратить время на ожидание завершения остальных тасков, не выполняя полезной работы.
Re[9]: Так подойдет, спасибо!
От: Nikolay_Ch Россия  
Дата: 14.09.17 15:04
Оценка:
SL>В случае, ContinueWith(async (_) => await Task.WhenAll(...)) все равно будет один вход в таску с await и один выход из нее.
SL>Я подозреваю, что в случае ContinueWith(() => Parallel.Invoke(...)) тред пул будет периодически давать время таске с Invoke и она будет просто тратить время на ожидание завершения остальных тасков, не выполняя полезной работы.
Ну так посмотрите в исходниках в чем отличие WhenAll от WaitAll и FastWaitAll
Думаю, что Вы удивитесь (а может и нет — смотря с какой стороны посмотреть)
Re[8]: Так подойдет, спасибо!
От: StatujaLeha на правах ИМХО
Дата: 14.09.17 15:10
Оценка:
Здравствуйте, _Raz_, Вы писали:

SL>>2. Ставим на паузу таску ContinueWith.

_R_>Это как?

Через await. Тред пул не будет передавать таску потоку из пула, пока не завершится таска под await-ом.

SL>>4. Идем обратно в таску ContinueWith и завершаем ее.

_R_>Откуда идем-то?

Тредпул мониторит, что завершилась таска под await-ом и это сигнал, что можно продолжить таску, которая содержит этот await.

SL>>Т.е. мы тратим на таску ContinueWith ресурсы только тогда, когда оно реально необходимо.

_R_>В рассматриваем примере этот таск завершится сразу после вызова WhenAll, то есть ресурсов на нее не будет затрачено совсем. Ресурсы будут использованы в таске, который создаст метод WhenAll.

Вот именно. А в случае Parallel.Invoke как будет? Я посмотрел исходники.
Как я понял, мы придем из первой строчки во вторую:
https://referencesource.microsoft.com/#mscorlib/system/threading/Tasks/Parallel.cs,215
https://referencesource.microsoft.com/#mscorlib/system/threading/Tasks/Task.cs,5180

Сценарий: первая таска из after выполняется очень быстро, оставшиеся медленно.
Получится, что Parallel.Invoke будет тратить ресурсы на то, чтобы периодически чекать, завершились ли задачи из after.
А этого можно избежать.

SL>>И как тредпул теперь поймет, что до завершения всех тасков after не надо тратить вычислительные ресурсы на таску, из которой запущен Parallel.Invoke?

_R_>Почему не надо то, если там работает Parallel.Invoke.

А смысл?
Туда нужно вернуться один раз, когда все задачи завершены.
В примере через await так и произойдет.
Re[9]: Так подойдет, спасибо!
От: _Raz_  
Дата: 14.09.17 16:44
Оценка:
Здравствуйте, StatujaLeha, Вы писали:

SL>Сценарий: первая таска из after выполняется очень быстро, оставшиеся медленно.

SL>Получится, что Parallel.Invoke будет тратить ресурсы на то, чтобы периодически чекать, завершились ли задачи из after.

Это откуда такое получается? В Parallel.Invoke нет никакого периодического чеканья.

SL>А смысл?


Не забывай, что в Parallel.Invoke передаются экшены и фабрика тасков для этих экшенов должна где-то выполняться.

SL>Туда нужно вернуться один раз, когда все задачи завершены.


А Parallel.Invoke никуда и не уходил, а это менее затратно по ресурсам

SL>В примере через await так и произойдет.


А дальше что? А дальше сразу конец метода. Вот и получилось, что держали-держали поток и сразу вышли Зачем держали
... << RSDN@Home 1.3.108 alpha 5 rev. 56>>
Re[10]: Так подойдет, спасибо!
От: StatujaLeha на правах ИМХО
Дата: 14.09.17 20:05
Оценка:
Здравствуйте, _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 tasks
                    for (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-ом не завершится.
Re[11]: Так подойдет, спасибо!
От: Nikolay_Ch Россия  
Дата: 14.09.17 20:23
Оценка:
Здравствуйте, StatujaLeha, Вы писали:

SL>Как бы в этом и нюанс, что в некоторых сценариях таска с Parallel.Invoke будет кушать вычислительные ресурсы, не направленные на выполнение задач after.

Погодите-ка... Но WhenAll работает так же, как и Parallell.Invoke. Где будет экономия?
Re[10]: Так подойдет, спасибо!
От: StatujaLeha на правах ИМХО
Дата: 14.09.17 20:28
Оценка:
Здравствуйте, Nikolay_Ch, Вы писали:

N_C>Ну так посмотрите в исходниках в чем отличие WhenAll от WaitAll и FastWaitAll

N_C>Думаю, что Вы удивитесь (а может и нет — смотря с какой стороны посмотреть)

Да, посмотрел.
WhenAll сам по себе не делает busy waiting, а WaitAll и FastWaitAll делают.
Выходит, что я убираю один busy waiting, если делаю через await.
Или я что-то не учел?
Re[12]: Так подойдет, спасибо!
От: StatujaLeha на правах ИМХО
Дата: 14.09.17 20:34
Оценка:
Здравствуйте, Nikolay_Ch, Вы писали:

N_C>Погодите-ка... Но WhenAll работает так же, как и Parallell.Invoke. Где будет экономия?


Не похоже.
https://referencesource.microsoft.com/#mscorlib/system/threading/Tasks/Task.cs,6016
https://referencesource.microsoft.com/#mscorlib/system/threading/Tasks/Task.cs,6089

Чекните. Там по завершении всех задач тупо флажок какой-то выставляется в Invoke, а busy waiting-а нет.
Re[11]: Так подойдет, спасибо!
От: Nikolay_Ch Россия  
Дата: 14.09.17 20:44
Оценка:
Здравствуйте, StatujaLeha, Вы писали:

SL>WhenAll сам по себе не делает busy waiting, а WaitAll и FastWaitAll делают.

Не делает, но (судя по исходникам) и параллельно задачи не запускает, т.е. выполняет их последовательно одну за одной...
Re[11]: Так подойдет, спасибо!
От: _Raz_  
Дата: 14.09.17 20:45
Оценка:
Здравствуйте, 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-а в синтаксическом сахаре. Если переписать на продолжениях суть изменится или нет?

И можно про то как тредпул отбирает поток у задачи?
... << RSDN@Home 1.3.108 alpha 5 rev. 56>>
Re[12]: Так подойдет, спасибо!
От: StatujaLeha на правах ИМХО
Дата: 14.09.17 20:54
Оценка: +2
Здравствуйте, Nikolay_Ch, Вы писали:

N_C>Не делает, но (судя по исходникам) и параллельно задачи не запускает, т.е. выполняет их последовательно одну за одной...


Он их вообще не запускает.

Поэтому в моем примере туда передаются задачи из Task.Run, т.е. уже запущенные(это обеспечит параллельность).
Task.WhenAll(Task.Run(after1), Task.Run(after2), Task.Run(after3))


А вот если сделать так, то ожидание будет вечным :D
Task.WhenAll(new Task(after1), new Task(after2), new Task(after3)).Wait()
Re[13]: Так подойдет, спасибо!
От: Nikolay_Ch Россия  
Дата: 14.09.17 20:58
Оценка:
Здравствуйте, StatujaLeha, Вы писали:

N_C>>Не делает, но (судя по исходникам) и параллельно задачи не запускает, т.е. выполняет их последовательно одну за одной...

SL>Он их вообще не запускает.
Согласен, недосмотрел.

SL>Поэтому в моем примере туда передаются задачи из Task.Run, т.е. уже запущенные(это обеспечит параллельность).

SL>
SL>Task.WhenAll(Task.Run(after1), Task.Run(after2), Task.Run(after3))
SL>

Ну, тогда у меня последний аргумент — Parallel может лучше суметь распараллелить таски, чем последовательный их запуск...
Re[12]: Так подойдет, спасибо!
От: StatujaLeha на правах ИМХО
Дата: 14.09.17 21:14
Оценка:
Здравствуйте, _Raz_, Вы писали:

_R_>Про чеканье стало понятно. Теперь возник вопрос в чем разница Parallel.Invoke и Task.WaitAll в разрезе этого чеканья.


Ок.
Между Parallel.Invoke и Task.WaitAll нет разницы.
А между Parallel.Invoke и Task.When All она есть.

_R_>Так и нет проблемы. Это ответ на "А смысл? Туда нужно вернуться один раз, когда все задачи завершены.".


Так и я думаю, что проблемы нет. Просто я не понял, к чему замечание "Не забывай, что в Parallel.Invoke передаются экшены и фабрика тасков для этих экшенов должна где-то выполняться."?
Это же ни на что не влияет.

_R_>Если ты видишь и понимаешь этот код, то смысл чего тебе не понятен?


Мне как раз все понятно

_R_>А что так загадочно? Рассказывай про сценарии и озвучь список ресурсов.


Я его уже озвучивал тебе: https://rsdn.org/forum/dotnet/6904266.1
Автор: StatujaLeha
Дата: 14.09.17


_R_>Это "точно" учитывает все if-ы внутри Parallel.Invoke и учитывает отсутствие переключение контекстов и дороговизну создания новых потоков (это про MaxDegreeOfParallelism)?


Ты о чем? В примере топикстартера Parallel.Invoke точно так же скидывает таски в тред пул, в котором все потоки уже созданы.
Таски в обоих вариантах запускаются идентично.

_R_>И что? Поток то все равно есть и он занимает ресурсы (не направленные на выполнение задач after ).


Это не так. Видимо, надо изучить матчасть.

_R_>Суть await-а в синтаксическом сахаре. Если переписать на продолжениях суть изменится или нет?

_R_>И можно про то как тредпул отбирает поток у задачи?

Ясно
Рекомендую изучить матчать: async/await вообще-то заставляют компилятор трансформировать функцию.
Google в помощь: "async await state machine".
Не то чтобы чтиво большое, но и не пару строк.
Re[14]: Так подойдет, спасибо!
От: StatujaLeha на правах ИМХО
Дата: 14.09.17 21:19
Оценка:
Здравствуйте, Nikolay_Ch, Вы писали:

N_C>Ну, тогда у меня последний аргумент — Parallel может лучше суметь распараллелить таски, чем последовательный их запуск...


В смысле?
При дефолтных аргументах(как у автора) что в моем примере, что внутри у Parallel.Invoke таски будут идентично запущены.

PS Task.Run() не дожидается окончания работы таски, которую он создал.
Re[13]: Так подойдет, спасибо!
От: _Raz_  
Дата: 14.09.17 22:51
Оценка:
Здравствуйте, StatujaLeha, Вы писали:

SL>Ок.

SL>Между Parallel.Invoke и Task.WaitAll нет разницы.
SL>А между Parallel.Invoke и Task.When All она есть.

Ок.

SL>Так и я думаю, что проблемы нет. Просто я не понял, к чему замечание "Не забывай, что в Parallel.Invoke передаются экшены и фабрика тасков для этих экшенов должна где-то выполняться."?

SL>Это же ни на что не влияет.

Это влияет на то, почему Parallel.Invoke работает в потоке ContinueWith — в этом же твое изначальное замечание
Автор: StatujaLeha
Дата: 14.09.17
было. А теперь, оказывается, что ни на что не влияет

_R_>>А что так загадочно? Рассказывай про сценарии и озвучь список ресурсов.

SL>Я его уже озвучивал тебе: https://rsdn.org/forum/dotnet/6904266.1
Автор: StatujaLeha
Дата: 14.09.17


Это вот этот: "Сценарий: первая таска из 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 переписать на продолжениях суть изменится или нет?
И можно про то как тредпул отбирает поток у задачи?
... << RSDN@Home 1.3.108 alpha 5 rev. 56>>
Re[15]: Так подойдет, спасибо!
От: Nikolay_Ch Россия  
Дата: 14.09.17 23:06
Оценка:
Здравствуйте, 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...
Re[16]: Так подойдет, спасибо!
От: StatujaLeha на правах ИМХО
Дата: 15.09.17 16:41
Оценка:
Здравствуйте, 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...

https://referencesource.microsoft.com/#mscorlib/system/threading/Tasks/Parallel.cs,318
Все равно это все придет к Wait.
Re[14]: Так подойдет, спасибо!
От: StatujaLeha на правах ИМХО
Дата: 15.09.17 19:16
Оценка:
Здравствуйте, _Raz_, Вы писали:

_R_>Это влияет на то, почему Parallel.Invoke работает в потоке ContinueWith — в этом же твое изначальное замечание
Автор: StatujaLeha
Дата: 14.09.17
было. А теперь, оказывается, что ни на что не влияет


Чего?
Мое изначальное замечание действительно в этом сообщении. Как мне кажется, оно ясно сформулировано

Как я понял по описанию Parallel.Invoke, получится, что ContinueWith запускает Parallel.Invoke и ждет, пока он отработает(т.е. таска ContinueWith будет периодически получать время на выполнение, даже если не все задачи завершены).


И мой вариант убирает это ожидание.

_R_>>>А что так загадочно? Рассказывай про сценарии и озвучь список ресурсов.

SL>>Я его уже озвучивал тебе: https://rsdn.org/forum/dotnet/6904266.1
Автор: StatujaLeha
Дата: 14.09.17


_R_>Это вот этот: "Сценарий: первая таска из after выполняется очень быстро, оставшиеся медленно."?

_R_>И из него ты делаешь вывод: "Как бы в этом и нюанс, что в некоторых сценариях таска с Parallel.Invoke будет кушать вычислительные ресурсы, не направленные на выполнение задач after."?

Да, я так думаю. Если первая таска выполнится быстро, то пойдет ожидание на ManualResetEventSlim, busy waiting.

SL>>Ты о чем?

_R_>Я о категоричности "это точно не менее затратно по ресурсам".

Ну ок. У меня пока замеров нету, так что снимаю это утверждение.
Тогда попрошу как-то подкрепить утверждение отсюда: http://rsdn.org/forum/dotnet/6904317.1
Автор: _Raz_
Дата: 14.09.17

А Parallel.Invoke никуда и не уходил, а это менее затратно по ресурсам


Как бы выходит, что в приведенном мной сценарии Parallel.Invoke действительно никуда не уходил и кушал ресурсы на ManualResetEventSlim.
Почему же это считается менее затратным?

_R_>Значит ты смотрел исходники Parallel.Invoke, сказал, что понял их, и все равно настаиваешь на категоричном "идентично"?


Согласен, слово "идентично" не подходит.
Если нет возражений, то заменю на "схожим образом".
Мой запуск
await Task.WhenAll(Task.Run(after1), Task.Run(after2), Task.Run(after3))


Запуск в Parallel.Invoke, https://referencesource.microsoft.com/#mscorlib/system/threading/Tasks/Parallel.cs,362
                    // Launch all actions as tasks
                    for (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.
            for (int i = 0; i < 5; i++)
            {
                Task.Run(async () =>
                         {
                             Console.WriteLine($"Thread before: {Thread.CurrentThread.ManagedThreadId}");
                             await Task.Delay(10);
                             Console.WriteLine($"Thread after: {Thread.CurrentThread.ManagedThreadId}");

                             Thread.SpinWait(1000);
                         })
                    .ContinueWith((_) => Console.WriteLine($"Continue thread: {Thread.CurrentThread.ManagedThreadId}"))
                    .Wait();
                Console.WriteLine();
            }

Thread before: 3
Thread after: 4
Continue thread: 4

Thread before: 4
Thread after: 6
Continue thread: 6

Thread before: 3
Thread after: 4
Continue thread: 4

Thread before: 6
Thread after: 3
Continue thread: 3

Thread before: 4
Thread after: 3
Continue thread: 4


_R_>Если await переписать на продолжениях суть изменится или нет?


Кинь плз пример кода, как ты предлагаешь это сделать.

_R_>И можно про то как тредпул отбирает поток у задачи?


Я механику в деталях не знаю, но выше привел пример, где зафиксировано такое поведение + ссылку на декомпилированную state machine, в которой тоже видно, что после await-а поток освобождается.
Re[15]: Так подойдет, спасибо!
От: _Raz_  
Дата: 15.09.17 20:54
Оценка:
Здравствуйте, StatujaLeha, Вы писали:

SL>Чего?


Того

SL>Мое изначальное замечание действительно в этом сообщении. Как мне кажется, оно ясно сформулировано

SL>

SL>Как я понял по описанию Parallel.Invoke, получится, что ContinueWith запускает Parallel.Invoke и ждет, пока он отработает(т.е. таска ContinueWith будет периодически получать время на выполнение, даже если не все задачи завершены).


А я тебе на это отвечаю — "Не забывай, что в Parallel.Invoke передаются экшены и фабрика тасков для этих экшенов должна где-то выполняться."
То есть он не ждет, а выполняет полезную работу. Так понятней, надеюсь

SL>И мой вариант убирает это ожидание.


Я отвечал на то что до скобок.

SL>Да, я так думаю. Если первая таска выполнится быстро, то пойдет ожидание на ManualResetEventSlim, busy waiting.


Так оно в любом случае будет. Не обязательно busy, но будет.

SL>Как бы выходит, что в приведенном мной сценарии Parallel.Invoke действительно никуда не уходил и кушал ресурсы на ManualResetEventSlim.

SL>Почему же это считается менее затратным?

Ты там смеющийся смайлик не заметил?

SL>Мой запуск

SL>Запуск в Parallel.Invoke, https://referencesource.microsoft.com/#mscorlib/system/threading/Tasks/Parallel.cs,362
SL>Разница в выделенном.

И в выделенном, и до выделенного, и после выделенного. Я все пытаюсь тебя подтолкнуть, что Parallel.Invoke это чуть больше запуска потоков — там и некоторая оптимизация и сбор ошибок.

SL>Можешь сам убедиться: в состояниях State Machine, соответствующих await-ам, делается запуск операции под await-ом и дальше выход из функции MoveNext.


Я спрашивал про тредпул. Как называется его метод отбирающий поток?

SL>Как объяснишь результаты работы кода ниже?

SL>Особенно выделенной части, когда исходный Action до await-а обрабатывается тредом 4, после await-а тредом 3, а Continue снова тредом 4.

Хватит уже щеки надувать. Ты меня не собеседуешь.

SL>Кинь плз пример кода, как ты предлагаешь это сделать.


    for (var i = 0; i < 5; i++)
    {
        Task.Factory.StartNew(() =>
            {
                Console.WriteLine($"Thread before: {Thread.CurrentThread.ManagedThreadId}");
                Task.Delay(10)
                    .ContinueWith(t =>
                    {
                        Console.WriteLine($"Thread after: {Thread.CurrentThread.ManagedThreadId}");
                        Thread.SpinWait(1000);
                    }, TaskContinuationOptions.AttachedToParent);
            })
            .ContinueWith(_ => Console.WriteLine($"Continue thread: {Thread.CurrentThread.ManagedThreadId}"))
            .Wait();
        Console.WriteLine();
    }


SL>Я механику в деталях не знаю


Вот сейчас было ваще на отличненько

SL> выше привел пример, где зафиксировано такое поведение + ссылку на декомпилированную state machine, в которой тоже видно, что после await-а поток освобождается.


Значит даю направление: тредпул во всей этой истории не играет ни малейшей роли и ничего ни у кого не отбирает. Так же как и await — да, да, он все еще синтаксический сахар и не важно во что он там разворачивается (кстати, ты и тут ошибаешься: разворачивается async, а не await). А играют тут шедулер и контекст выполнения.
... << RSDN@Home 1.3.108 alpha 5 rev. 56>>
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.