Почему ThreadPool решает использовать точный контекстный поток, который называется Task.Wait? - PullRequest
0 голосов
/ 30 марта 2020

Почему ThreadPool решает использовать точный контекстный поток, который вызвал Task.Wait?

Существует вопрос . По некоторым причинам я не вижу кнопку комментария ни для вопроса, ни для любого из его ответов. Итак, я задаю связанный вопрос в отдельной теме.

В связанном вопросе есть ответ, который указывает на блог . Согласно этому блогу справедливо следующее утверждение:

Есть фрагмент кода:

// My "library" method.
public static async Task<JObject> GetJsonAsync(Uri uri)
{
  // (real-world code shouldn't use HttpClient in a using block; this is just example code)
  using (var client = new HttpClient())
  {
    var jsonString = await client.GetStringAsync(uri);
    return JObject.Parse(jsonString);
  }
}

// My "top-level" method.
public void Button1_Click(...)
{
  var jsonTask = GetJsonAsync(...);
  textBox1.Text = jsonTask.Result;
}

И есть объяснение возникновения тупика:

  1. Метод верхнего уровня вызывает GetJsonAsync (в контексте UI / ASP. NET).
  2. GetJsonAsync запускает запрос REST, вызывая HttpClient.GetStringAsync (все еще в контексте).
  3. GetStringAsync возвращает незавершенный Task, указывая, что запрос REST не завершен.
  4. GetJsonAsync ожидает Task, возвращаемое GetStringAsync. Контекст захвачен и будет использоваться для продолжения запуска метода GetJsonAsync позже. GetJsonAsync возвращает незавершенное задание, указывая, что метод GetJsonAsync не завершен.
  5. Метод верхнего уровня синхронно блокирует задачу, возвращаемую GetJsonAsync. Это блокирует поток контекста.
  6. … В конце концов, запрос REST будет выполнен. Это завершает Task, который был возвращен GetStringAsync.
  7. Теперь продолжение для GetJsonAsync готово к запуску и ожидает, когда контекст станет доступным, чтобы его можно было выполнить в контексте.
  8. Тупик. Метод верхнего уровня блокирует поток контекста, ожидая завершения GetJsonAsync, а GetJsonAsync ожидает освобождения контекста, чтобы он мог завершиться.

И мой вопрос, в частности, касается шага 7. Почему ThreadPool решает взять заблокированный поток и ждать, пока он не разблокируется, попросить этот поток запустить код? Почему бы просто не взять другую ветку?

1 Ответ

1 голос
/ 30 марта 2020

GetJsonAsync не выполняется в потоке произвольного пула потоков. Он выполняется в потоке контекста.

В соответствии с примером кода задача GetJsonAsync была создана событием нажатия кнопки, которое выполняется в потоке пользовательского интерфейса (потоке контекста). Когда задача находится в ожидании, текущий контекст (в данном случае контекст синхронизации пользовательского интерфейса) фиксируется. После завершения задачи возобновление продолжается в том же контексте.

На шаге 7 задача пытается вернуться в поток пользовательского интерфейса, но поток пользовательского интерфейса блокируется .Result, ожидая, что задача будет завершена. возвращение. Таким образом, возникает тупик.

Я заметил, что упомянутый вопрос задавался о ASP. NET приложениях WebApi. Так что просто хочу уточнить некоторые моменты:

ASP. Net У WebAPI есть специальный контекст синхронизации, но он отличается от контекста пользовательского интерфейса. Существует только один поток пользовательского интерфейса, поэтому контекст планирует обратные вызовы / продолжения только для потока пользовательского интерфейса.

Однако контекст синхронизации для ASP. Net WebAPI не захватывает один единственный поток. Код ASP. Net может выполняться в другом / произвольном потоке. Контекст отвечает за восстановление данных потока и обеспечение того, чтобы продолжения были объединены в цепочку в порядке очереди.

...