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.
Это надстройка 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 будет использовать несколько экземпляров SyncCtx, которые все ссылаются на один и тот же диспетчер (по одному на окно IIRC верхнего уровня).
Вот через год. На всякий случай, если это кому-то поможет, я подтвердил, что вам нужно установить контекст синхронизации перед каждой асинхронной операцией; поэтому настроить его один раз в автозагрузке недостаточно. Но вы можете создать один глобальный объект (например, переменную internal
в вашем классе ThisAddIn
), который затем будете передавать каждый раз при вызове функции SynchronizationContext.SetSynchronizationContext
.
Два связанных вопроса: 1. Требуется ли устанавливать контекст синхронизации перед каждым ожидаемым или я могу сделать это один раз при запуске моей надстройки? 2. Могу ли я как-то получить встроенный/по умолчанию контекст синхронизации WPF, или мне нужно сделать это как
new DispatcherSynchronizationContext()
? (Я просмотрел библиотеки, и самое близкое, что я нашел, былоSystem.Windows.Application.Current.Dispatcher
, но это не дает мне доступа к объекту контекста).