Поток STA используется в Winforms, но не при выполнении в качестве консольного приложения

У меня есть сборка, которая работает под потоком STA в песочнице от третьей стороны, в этом потоке я создал дуплексный клиент WCF, которому необходимо выполнять методы в исходном потоке STA.

Текущая реализация работает нормально, в обратном вызове Duplex я получаю контекст синхронизации потока STA следующим образом и использую его для обратной отправки в поток STA:

private readonly SynchronizationContext _syncContext = AsyncOperationManager.SynchronizationContext;

Все это выполняется в WinForm, инициализированном в потоке STA, отлично... но мне нужно переместить дуплексный прокси-сервер WCF, чтобы вместо этого он работал под экземпляром класса в основном потоке STA. Когда я удаляю winform, я получаю совершенно новый поток из вышеупомянутого SynchronizationContext.

Чтобы уточнить:

Winforms:-

  • Запуск дуплексного прокси-сервера WCF в потоке STA — ManagedThreadId = 1
  • Получение дуплексного обратного вызова с сервера — ManagedThreadId = 5
  • Опубликовать в методе события обратного вызова с помощью AsyncOperationManager.SynchronizationContext — ManagedThreadId = 1

Без WinForm (экземпляр класса): -

  • Запуск дуплексного прокси-сервера WCF в потоке STA — ManagedThreadId = 1
  • Получение дуплексного обратного вызова с сервера — ManagedThreadId = 6
  • Опубликовать в методе события обратного вызова с помощью AsyncOperationManager.SynchronizationContext — ManagedThreadId = 11

Выполнение в потоке 11 вместо 1 означает, что мои методы не работают должным образом в песочнице, нет никакой разницы в коде между вариантами, кроме того, который выполняется под winform. Кто-нибудь знает, как я могу сохранить выполнение метода дуплексного обратного вызова в основном потоке STA без использования winform?

Можете ли вы опубликовать свой Main метод? Без этого предполагается следующий диагноз. В вашем заголовке говорится, что это консольное приложение, и у него есть два потенциальных источника. Во-первых, вы украсили метод Main с помощью [STAThread]? Вторая и более вероятная проблема заключается в том, что консольное приложение не устанавливает контекст синхронизации на основе типа потока; при создании контекста он является экземпляром класса SynchronizationContext, и его Почтовый метод использует пул потоков.

TnTinMn 18.03.2019 02:50

Зачем вообще нужен STA? Используете ли вы COM-объекты? Можете ли вы использовать форму (даже невидимую) в своем консольном приложении? Если да, вы можете использовать контекст синхронизации этой формы.

Simon Mourier 18.03.2019 12:47
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
290
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы получаете контекст синхронизации, используя свойство AsyncOperationManager.SynchronizationContext. Это свойство использует SynchronizationContext.Current под капотом.

Это означает, что полученный SynchronizationContext зависит от среды, в которой вы получаете доступ к свойству:

  • поток, в котором вы обращаетесь к свойству, и
  • тип приложения.

Как вы можете прочитать в документах:

The default implementation is the free-threaded implementation.

Таким образом, если контекст синхронизации текущего потока не установлен, вы получите экземпляр SynchronizationContext с бесплатным потоком по умолчанию. Это будет Send обратные вызовы путем синхронного выполнения в вызывающем потоке и Post обратные вызовы в ThreadPool (так что «случайные» рабочие потоки подберут их).

В приложении Windows Forms основной поток SynchronizationContext будет инициализирован экземпляром WindowsFormsSynchronizationContext для вас. Этот экземпляр будет Post выполнять обратные вызовы основного потока пользовательского интерфейса.

В приложении WPF это будет DispatcherSynchronizationContext.

В консольном приложении не будет SynchronizationContext для основного потока. Таким образом, срабатывает вариант с бесплатным потоком, о котором я упоминал выше, поэтому вы получаете экземпляр с бесплатным потоком SynchronizationContext, который отправляет сообщения в ThreadPool. Это в значительной степени объясняет поведение, которое вы наблюдаете.

Если вам нужна эта синхронизация, вы можете реализовать свой собственный аффинный поток SynchronizationContext для основного потока вашего консольного приложения. Хотя это непросто. В консольном приложении у вас нет ни цикла обработки сообщений, ни диспетчера, который мог бы управлять очередью обратного вызова. Вы можете взглянуть на этот отличный ответ Стивена Клири, чтобы получить представление об асинхронном SynchronizationContext. Однако вам нужно будет создать «основной цикл» вручную.

Другие вопросы по теме