Await не возобновляется в исходном контексте

await по какой-то причине не возобновляется контекст вызова. Я вызываю метод async (на самом деле это иерархия нескольких методов async). Все идет хорошо, пока я, наконец, не достиг DataService, который использует ExecuteAsync() RestSharp. Последний звонок выглядит так:

IRestResponse Response = await rc.ExecuteAsync(request); //rc is a RestClient object

Я тщательно его отладил, и на каждом уровне следующая строка возвращает true даже непосредственно перед вызовом ExecuteAsync:

System.Threading.Thread.CurrentThread == System.Windows.Application.Current.Dispatcher.Thread

Только после того, как ExecuteAsync() возвращается с данными сервера, строка выше становится false, указывая на то, что указанная выше await не возобновилась в вызывающем потоке. После этого более высокие уровни, которые вызвали эту асинхронную операцию и теперь хотят назначить возвращенные данные пользовательскому интерфейсу, выдают печально известное исключение потока STA.

Что я делаю не так? Я уже сжег несколько часов и просмотрел несколько сообщений SO и веб-статей, но, похоже, это не работает. Я также добавил ConfigureAwait(true), чтобы явно попросить его возобновить работу при вызове контекста, но этого не произошло.

Это надстройка VSTO Word. Я использую его с WPF.

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

Ответы 1

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

Это надстройка VSTO Word.

На самом деле это источник проблемы.

Фактический «контекст», захваченный await, равен SynchronizationContext.Current (если только это не null, в этом случае это TaskScheduler.Current). Приложения пользовательского интерфейса, такие как WinForms и WPF, предоставляют свои собственные SynchronizationContext, именно так await возобновляется в потоке пользовательского интерфейса.

Кроме надстроек Office. Я не уверен, почему, но они не предоставляют SynchronizationContext. Поэтому, если вы хотите использовать await в надстройке Office, вам нужно указать SynchronizationContext.

Я считаю, что надстройка Office на основе WPF сможет сделать это в начале каждого события async:

SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext());

Но на самом деле я никогда не пробовал это. :)

Два связанных вопроса: 1. Требуется ли устанавливать контекст синхронизации перед каждым ожидаемым или я могу сделать это один раз при запуске моей надстройки? 2. Могу ли я как-то получить встроенный/по умолчанию контекст синхронизации WPF, или мне нужно сделать это как new DispatcherSynchronizationContext()? (Я просмотрел библиотеки, и самое близкое, что я нашел, было System.Windows.Application.Current.Dispatcher, но это не дает мне доступа к объекту контекста).

dotNET 16.12.2020 07:44

1. Я думаю, вы могли бы сделать это только один раз, но вам нужно проверить это, чтобы быть уверенным. 2. Я не знаю, как это сделать. Но я бы не беспокоился об этом; в последний раз, когда я проверял, сам WPF будет использовать несколько экземпляров SyncCtx, которые все ссылаются на один и тот же диспетчер (по одному на окно IIRC верхнего уровня).

Stephen Cleary 16.12.2020 13:37

Вот через год. На всякий случай, если это кому-то поможет, я подтвердил, что вам нужно установить контекст синхронизации перед каждой асинхронной операцией; поэтому настроить его один раз в автозагрузке недостаточно. Но вы можете создать один глобальный объект (например, переменную internal в вашем классе ThisAddIn), который затем будете передавать каждый раз при вызове функции SynchronizationContext.SetSynchronizationContext.

dotNET 10.10.2021 19:32

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