it-swarm.com.ru

Вы должны поместить Task.Run в метод, чтобы сделать его асинхронным?

Я пытаюсь понять асинхронное ожидание в простейшей форме. Я хочу создать очень простой метод, который добавляет два числа ради этого примера, конечно, это совсем не время обработки, это просто вопрос формулировки примера здесь.

Пример 1

private async Task DoWork1Async()
{
    int result = 1 + 2;
}

Пример 2

private async Task DoWork2Async()
{
    Task.Run( () =>
    {
        int result = 1 + 2;
    });
}

Если я жду DoWork1Async(), будет ли код выполняться синхронно или асинхронно?

Нужно ли обернуть код синхронизации Task.Run, чтобы сделать метод ожидаемым и асинхронным, чтобы не блокировать поток пользовательского интерфейса?

Я пытаюсь выяснить, является ли мой метод Task или возвращает Task<T>, нужно ли мне обернуть код с Task.Run, чтобы сделать его асинхронным.

Я уверен, что это глупый вопрос, но я вижу в сети примеры, когда люди ждут кода, в котором нет ничего асинхронного и не заключенного в Task.Run или StartNew.

274
Neal

Во-первых, давайте проясним некоторую терминологию: "асинхронный" (async) означает, что он может вернуть управление вызывающему потоку до его запуска. В методе async эти точки "доходности" являются выражениями await.

Это очень отличается от термина "асинхронный", так как (неправильно) используется в документации MSDN в течение многих лет для обозначения "выполняется в фоновом потоке".

Чтобы еще больше запутать проблему, async очень отличается от "awaitable"; Есть несколько методов async, возвращаемые типы которых не являются ожидаемыми, и многие методы, возвращающие ожидаемые типы, которые не являются async.

Достаточно того, что они не являются ; вот что они :

  • Ключевое слово async допускает асинхронный метод (то есть, он допускает выражения await). Методы async могут возвращать Task, Task<T> или (если необходимо) void.
  • Любой тип, который следует за определенной моделью, может быть ожидаемым. Наиболее распространенными ожидаемыми типами являются Task и Task<T>.

Итак, если мы сформулируем ваш вопрос следующим образом: "Как я могу выполнить операцию в фоновом потоке таким образом, чтобы это было ожидаемо", ответ будет использовать Task.Run:

private Task<int> DoWorkAsync() // No async because the method does not need await
{
  return Task.Run(() =>
  {
    return 1 + 2;
  });
}

(Но этот шаблон плохой подход; см. Ниже).

Но если ваш вопрос "как мне создать метод async, который может возвращать вызывающему объекту вместо блокировки", ответ заключается в том, чтобы объявить метод async и использовать await для его "уступающих" точек:

private async Task<int> GetWebPageHtmlSizeAsync()
{
  var client = new HttpClient();
  var html = await client.GetAsync("http://www.example.com/");
  return html.Length;
}

Таким образом, основной шаблон состоит в том, чтобы код async зависел от "awaitables" в его выражениях await. Этими "ожидаемыми" могут быть другие методы async или просто обычные методы, возвращающие ожидаемые значения. Обычные методы, возвращающие Task/Task<T>, могут использовать Task.Run для выполнения кода в фоновом потоке или (чаще) они могут использовать TaskCompletionSource<T> или один из его ярлыков (TaskFactory.FromAsync, Task.FromResult и т.д.) , Я не рекомендую переносить весь метод в Task.Run; Синхронные методы должны иметь синхронные подписи, и потребитель должен решать, должен ли он быть заключен в Task.Run:

private int DoWork()
{
  return 1 + 2;
}

private void MoreSynchronousProcessing()
{
  // Execute it directly (synchronously), since we are also a synchronous method.
  var result = DoWork();
  ...
}

private async Task DoVariousThingsFromTheUIThreadAsync()
{
  // I have a bunch of async work to do, and I am executed on the UI thread.
  var result = await Task.Run(() => DoWork());
  ...
}

У меня есть async/await intro в моем блоге; в конце несколько хороших ресурсов для продолжения. Документы MSDN для async также необычайно хороши.

532
Stephen Cleary

При оформлении метода с помощью async важно помнить, что, по крайней мере, существует один ожидают Оператор внутри метода. В вашем примере я бы перевел это, как показано ниже, используя TaskCompletionSource .

private Task<int> DoWorkAsync()
{
    //create a task completion source
    //the type of the result value must be the same
    //as the type in the returning Task
    TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
    Task.Run(() =>
    {
        int result = 1 + 2;
        //set the result to TaskCompletionSource
        tcs.SetResult(result);
    });
    //return the Task
    return tcs.Task;
}

private async void DoWork()
{
    int result = await DoWorkAsync();
}
21
Ronald Ramos

Когда вы используете Task.Run для запуска метода, Task получает поток из пула потоков для запуска этого метода. Таким образом, с точки зрения потока пользовательского интерфейса, он является "асинхронным", поскольку не блокирует поток пользовательского интерфейса. Это хорошо для настольных приложений, так как обычно вам не нужно много потоков для обеспечения взаимодействия с пользователем.

Однако для веб-приложения каждый запрос обслуживается потоком пула потоков, и, таким образом, количество активных запросов может быть увеличено путем сохранения таких потоков. Часто использование потоков пула потоков для имитации асинхронной операции не масштабируется для веб-приложений.

True Async не обязательно предполагает использование потока для операций ввода-вывода, таких как доступ к файлу/БД и т.д. Вы можете прочитать это, чтобы понять, почему для операции ввода-вывода не нужны потоки. http://blog.stephencleary.com/2013/11/there-is-no-thread.html

В вашем простом примере это чисто вычисление с привязкой к процессору, поэтому использование Task.Run вполне подойдет.

11
zheng yu