Я хотел бы сначала поставить код, а затем объяснить ситуацию и задать свой вопрос на основе этого:
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
}
private async void Button_Click_2(object sender, RoutedEventArgs e) {
var result = await GetValuesAsync();
Foo.Text += result;
}
public async Task<string> GetValuesAsync() {
using (var httpClient = new HttpClient()) {
var response = await httpClient
.GetAsync("http://www.google.com")
.ConfigureAwait(continueOnCapturedContext: false);
// This is the continuation for the httpClient.GetAsync method.
// We shouldn't get back to sync context here
// Cuz the continueOnCapturedContext is set to *false*
// for the Task which is returned from httpClient.GetAsync method
var html = await GetStringAsync();
// This is the continuation for the GetStringAsync method.
// Should I get back to sync context here?
// Cuz the continueOnCapturedContext is set to *true*
// for the Task which is returned from GetStringAsync
// However, GetStringAsync may be executed in another thread
// which has no knowledge for the sync context
// because the continueOnCapturedContext is set to *false*
// for the Task which is returned from httpClient.GetAsync method.
// But, on the other hand, GetStringAsync method also has a
// chance to be executed in the UI thread but we shouldn't be
// relying on that.
html += "Hey...";
Foo.Text = html;
return html;
}
}
public async Task<string> GetStringAsync() {
await Task.Delay(1000);
return "Done...";
}
}
Это довольно простой пример WPF, который работает на .NET 4.5 и, вероятно, не имеет большого смысла, но это должно помочь мне объяснить мою ситуацию.
У меня есть кнопка на экране с асинхронным кликом. Когда вы посмотрите на код GetValuesAsync
, вы увидите использование ключевого слова await
дважды. При первом использовании я установил параметр continueOnCapturedContext
метода Task.ConfigureAwait
на false
. Таким образом, это означает, что я не обязательно хочет продолжить мое продолжение внутри SynchronizationContext.Current
. Пока все хорошо.
Во втором использовании await
(с методом GetStringAsync
) я не вызывал метод ConfigureAwait
. Итак, я в основном указал, что я хочу вернуться к текущему контексту синхронизации для продолжения метода GetStringAsync
. Итак, как вы можете видеть, я пытаюсь установить свойство TextBlock.Text
(которое принадлежит объекту UI) внутри продолжения.
Когда я запускаю приложение и нажимаю кнопку, я получаю исключение, предоставляющее мне следующее сообщение:
Вызывающий поток не может получить доступ к этому объекту, потому что другой нить владеет им.
Сначала это не имело смысла для меня, и я подумал, что обнаружил ошибку, но потом понял, что GetStringAsync
может быть выполнен в другом потоке (весьма вероятно), который отличается от потока пользовательского интерфейса и не знает для контекста синхронизации, поскольку для параметра continueOnCapturedContext
установлено значение false
для Task
, которое возвращается из метода httpClient.GetAsync
.
Здесь ли это? Кроме того, в этом случае существует вероятность того, что метод GetStringAsync
будет отправлен обратно в поток пользовательского интерфейса bacuse httpClient.GetAsync продолжение метода может выполняться внутри потока пользовательского интерфейса?
У меня также есть несколько комментариев внутри кода. В связи с моими вопросами и комментариями внутри кода я ничего не теряю здесь?