У меня есть сборка, которая работает под потоком STA в песочнице от третьей стороны, в этом потоке я создал дуплексный клиент WCF, которому необходимо выполнять методы в исходном потоке STA.
Текущая реализация работает нормально, в обратном вызове Duplex я получаю контекст синхронизации потока STA следующим образом и использую его для обратной отправки в поток STA:
private readonly SynchronizationContext _syncContext = AsyncOperationManager.SynchronizationContext;
Все это выполняется в WinForm, инициализированном в потоке STA, отлично... но мне нужно переместить дуплексный прокси-сервер WCF, чтобы вместо этого он работал под экземпляром класса в основном потоке STA. Когда я удаляю winform, я получаю совершенно новый поток из вышеупомянутого SynchronizationContext.
Чтобы уточнить:
Winforms:-
Без WinForm (экземпляр класса): -
Выполнение в потоке 11 вместо 1 означает, что мои методы не работают должным образом в песочнице, нет никакой разницы в коде между вариантами, кроме того, который выполняется под winform. Кто-нибудь знает, как я могу сохранить выполнение метода дуплексного обратного вызова в основном потоке STA без использования winform?
Зачем вообще нужен STA? Используете ли вы COM-объекты? Можете ли вы использовать форму (даже невидимую) в своем консольном приложении? Если да, вы можете использовать контекст синхронизации этой формы.





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