Ответ 1
Причины путаницы:
- Пользовательский интерфейс не имеет "специального"
TaskScheduler
. Случай по умолчанию для кода, запущенного в потоке пользовательского интерфейса, состоит в том, чтоTaskScheduler.Current
хранитThreadPoolTaskScheduler
иSynchronizationContext.Current
сохраняетWindowsFormsSynchronizationContext
(или соответствующий в других приложениях пользовательского интерфейса) -
ThreadPoolTaskScheduler
вTaskScheduler.Current
не обязательно означает, чтоTaskScheduler
используется для запуска текущей части кода. Это также означаетTaskSchdeuler.Current == TaskScheduler.Default
и поэтому "не используетсяTaskScheduler
" . -
TaskScheduler.FromCurrentSynchronizationContext()
не возвращает "acutal"TaskScheduler
. Он возвращает "прокси", который отправляет задания прямо на захваченныйSynchronizationContext
.
Итак, если вы запустите свой тест перед запуском задачи (или в любом другом месте), вы получите тот же результат, что и после ожидания:
MessageBox.Show(TaskScheduler.Current == TaskScheduler.FromCurrentSynchronizationContext()); // False
Потому что TaskScheduler.Current
есть ThreadPoolTaskScheduler
, а TaskScheduler.FromCurrentSynchronizationContext()
возвращает SynchronizationContextTaskScheduler
.
Это поток вашего примера:
- Вы создаете новый
SynchronizationContextTaskScheduler
из пользовательского интерфейсаSynchronizationContext
(т.е.WindowsFormsSynchronizationContext
). - Запланируйте задачу, созданную с помощью
Task.Factory.StartNew
, на этомTaskScheduler
. Поскольку это просто "прокси", он помещает делегата вWindowsFormsSynchronizationContext
, который вызывает его в потоке пользовательского интерфейса. - Синхронная часть (часть до первого ожидания) этого метода выполняется в потоке пользовательского интерфейса, будучи связана с
SynchronizationContextTaskScheduler
. - Метод достигает ожидания и приостанавливается при захвате
WindowsFormsSynchronizationContext
. - Когда продолжение возобновляется после ожидания, оно отправляется на
WindowsFormsSynchronizationContext
, а неSynchronizationContextTaskScheduler
, посколькуSynchronizationContext
имеет приоритет (это можно увидеть вTask.SetContinuationForAwait
). Затем он регулярно запускается в потоке пользовательского интерфейса без каких-либо специальныхTaskScheduler
, поэтомуTaskScheduler.Current == TaskScheduler.Default
.
Итак, созданная задача выполняется на прокси-сервере TaskScheduler
, который использует SynchronizationContext
, но продолжение после ожидания отправляется на этот SynchronizationContext
, а не TaskScheduler
.