Почему доступ к Task.Result в синхронном режиме не вызывает тупиковой ситуации? - PullRequest
0 голосов
/ 24 апреля 2019

Как мы все знаем, доступ к свойству Result задачи в потоке пользовательского интерфейса и синхронный режим приведет к взаимоблокировке.

Как теоретически, код будет тупиковым, но нет. Не могли бы вы объяснить, почему?

// My "library" method.
public static async Task<JObject> GetJsonAsync(Uri uri)
{
  using (var client = new HttpClient())
  {
    var jsonString = await client.GetStringAsync(uri);
    return JObject.Parse(jsonString);
  }
}

//MVC action
public ActionResult Index()
{
      var result = System.Threading.Tasks.Task.Run(async () => await GetJsonAsync(...)).Result; // deadlock is expectation but not :(

    ...
}

Я думал, что System.Threading.Tasks.Task.Run(async () => await GetJsonAsync(...)).Result в некотором роде схож с GetJsonAsync(...).Result, но не.

GetJsonAsync(...)).Result будет в тупике но System.Threading.Tasks.Task.Run(async () => await GetJsonAsync(...)).Result нет.

1 Ответ

3 голосов
/ 24 апреля 2019

Result само по себе не вызывает тупик .Он вызывает взаимоблокировку при вызове из однопоточного контекста , если есть await для этой задачи, которая также нуждается в этом контексте.

Подробнее :

  • await по умолчанию захватывает контекст и возобновляет работу в этом контексте.(Вы можете использовать ConfigureAwait(false), чтобы переопределить это поведение по умолчанию и продолжить работу в потоке пула потоков.)
  • Result блокирует текущий поток до тех пор, пока Task не будет завершен.(Вы можете использовать await для асинхронного использования задачи, чтобы избежать блокировки потока.)
  • Некоторые контексты являются однопоточными;т.е. они допускают только один поток за раз.Например, ASP.NET Classic имеет однопоточный контекст запроса.(Вы можете использовать Task.Run для запуска кода в потоке пула потоков с контекстом пула потоков, который не является однопоточным контекстом.)

Итак, чтобы получить взаимоблокировку, вам нужноиметь await, который захватывает однопоточный контекст, а затем блокировать поток внутри этого контекста (например, вызывая Result для этой задачи).Для await требуется контекст для завершения Task, но контекст допускает только один поток за раз, а Result удерживает поток заблокированным в этом контексте до завершения Task.

В вашем примере вы вызываете GetJsonAsync внутри Task.Run, который запускает его в пуле потоков.Таким образом, await в GetJsonAsyncawait в делегате, переданном в Task.Run) захватывает контекст пула потоков, а не контекст потока запросов ASP.NET.Затем ваш код вызывает Result, что блокирует поток запроса ASP.NET (и его контекст), но, поскольку await этот контекст не нужен, тупика нет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...