client.GetAsync(url).Result
синхронно блокируется. resp.Content.ReadAsStreamAsync()
на самом деле ничего не ждет. Task
уже завершен, потому что HttpCompletionOption.ResponseContentRead
используется в GetAsync
, поэтому весь блок кода здесь представляет собой синхронный код, претендующий на асинхронность.
В общем, вы никогда не должны использовать .Result
или .Wait
или Task.Wait*
- если вам абсолютно необходимо, используйте GetAwaiter().GetResult()
, который не генерирует исключения, заключенные в AggregateException
s, для которых вы можете не иметь catch
, но даже этого следует избегать, как чумы. Вы вправе использовать async
до конца.
Взаимная блокировка является проблемой только в контексте, который хочет вернуться обратно в исходный поток (например, пользовательский интерфейс или ASP.NET), и этот поток блокируется синхронным ожиданием. Если вы всегда используете ConfigureAwait(false)
на каждые await
, , кроме случаев , вы знаете, что вам действительно нужно сохранить контекст в продолжении (обычно только в обработчике событий пользовательского интерфейса верхнего уровня, потому что вы необходимо что-то обновить в пользовательском интерфейсе или на верхнем уровне контроллера ASP.NET), тогда вы должны быть в безопасности.
Синхронная блокировка внутри асинхронного кода также не годится, но не приведет к тупику. Использование await
с ConfigureAwait(true)
(по умолчанию) внутри синхронного ожидания в контексте, который хочет вернуться назад к определенному потоку, приведет к взаимоблокировке.