Вы получаете контекст синхронизации, используя свойство AsyncOperationManager.SynchronizationContext
.Это свойство использует SynchronizationContext.Current
под капотом.
Это означает, что полученный SynchronizationContext
зависит от среды, к которой вы обращаетесь к свойству:
- поток, в котором вы находитесьдоступ к свойству и
- тип приложения.
Как вы можете прочитать в документах :
Реализация по умолчанию - это свободнопоточная реализация.
Таким образом, если контекст синхронизации текущего потока не задан, вы получите экземпляр по умолчанию со свободной резьбой SynchronizationContext
.Это будет Send
обратные вызовы при синхронном выполнении в потоке вызывающей стороны и Post
обратные вызовы ThreadPool
(так что «случайные» рабочие потоки будут их забирать).
В приложении Windows Forms основной потокSynchronizationContext
будет инициализирован для вас WindowsFormsSynchronizationContext
экземпляром.Этот экземпляр будет Post
обратными вызовами для основного потока пользовательского интерфейса.
В приложении WPF это будет DispatcherSynchronizationContext
.
В консольном приложении не будет SynchronizationContext
для основной темы.Таким образом, включается опция со свободной резьбой, о которой я упоминал выше, и вы получаете экземпляр со свободной резьбой SynchronizationContext
, который отправляет в ThreadPool
.Это в значительной степени объясняет поведение, которое вы наблюдаете.
Если вам нужна эта синхронизация, вы можете реализовать свой собственный аффинный поток SynchronizationContext
для основного потока вашего консольного приложения.Это не легко, хотя.В консольном приложении у вас нет цикла обработки сообщений и диспетчера, который мог бы управлять очередью обратного вызова.Вы можете взглянуть на этот отличный ответ Стивена Клири для идеи асинхронного SynchronizationContext
.Вам нужно будет создать «основной цикл» вручную.