Какая разница между Task.Start/Wait и Async/Await?

Возможно, мне что-то не хватает, но в чем разница между:

public void MyMethod()
{
  Task t = Task.Factory.StartNew(DoSomethingThatTakesTime);
  t.Wait();
  UpdateLabelToSayItsComplete();
}

public async void MyMethod()
{
  var result = Task.Factory.StartNew(DoSomethingThatTakesTime);
  await result;
  UpdateLabelToSayItsComplete();
}

private void DoSomethingThatTakesTime()
{
  Thread.Sleep(10000);
}

Ответ 1

Возможно, мне что-то не хватает

Вы находитесь.

В чем разница между выполнением Task.Wait и await task?

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

Task.Wait блокируется до завершения задачи - вы игнорируете своего друга, пока задача не будет завершена. await продолжает обрабатывать сообщения в очереди сообщений, а когда задача завершена, она выдает сообщение, в котором говорится: "Заберите, где вы остановились после этого ожидания". Вы разговариваете со своим другом, и когда в разговоре происходит перерыв, приходит суп.

Ответ 2

Чтобы продемонстрировать ответ Эрика, вот какой код:

public void ButtonClick(object sender, EventArgs e)
{
  Task t = new Task.Factory.StartNew(DoSomethingThatTakesTime);
  t.Wait();  
  //If you press Button2 now you won't see anything in the console 
  //until this task is complete and then the label will be updated!
  UpdateLabelToSayItsComplete();
}

public async void ButtonClick(object sender, EventArgs e)
{
  var result = Task.Factory.StartNew(DoSomethingThatTakesTime);
  await result;
  //If you press Button2 now you will see stuff in the console and 
  //when the long method returns it will update the label!
  UpdateLabelToSayItsComplete();
}

public void Button_2_Click(object sender, EventArgs e)
{
  Console.WriteLine("Button 2 Clicked");
}

private void DoSomethingThatTakesTime()
{
  Thread.Sleep(10000);
}

Ответ 3

Этот пример демонстрирует разницу очень четко. С async/await вызывающий поток не будет блокироваться и продолжать выполнение.

static void Main(string[] args)
{
    WriteOutput("Program Begin");
    // DoAsTask();
    DoAsAsync();
    WriteOutput("Program End");
    Console.ReadLine();
}

static void DoAsTask()
{
    WriteOutput("1 - Starting");
    var t = Task.Factory.StartNew<int>(DoSomethingThatTakesTime);
    WriteOutput("2 - Task started");
    t.Wait();
    WriteOutput("3 - Task completed with result: " + t.Result);
}

static async Task DoAsAsync()
{
    WriteOutput("1 - Starting");
    var t = Task.Factory.StartNew<int>(DoSomethingThatTakesTime);
    WriteOutput("2 - Task started");
    var result = await t;
    WriteOutput("3 - Task completed with result: " + result);
}

static int DoSomethingThatTakesTime()
{
    WriteOutput("A - Started something");
    Thread.Sleep(1000);
    WriteOutput("B - Completed something");
    return 123;
}

static void WriteOutput(string message)
{
    Console.WriteLine("[{0}] {1}", Thread.CurrentThread.ManagedThreadId, message);
}

Выход DoAsTask:

[1] Program Begin
[1] 1 - Starting
[1] 2 - Task started
[3] A - Started something
[3] B - Completed something
[1] 3 - Task completed with result: 123
[1] Program End

Выход DoAsAsync:

[1] Program Begin
[1] 1 - Starting
[1] 2 - Task started
[3] A - Started something
[1] Program End
[3] B - Completed something
[3] 3 - Task completed with result: 123

Обновление: улучшенный пример, показывая идентификатор потока на выходе.

Ответ 4

В этом примере практически не много. Если вы ожидаете задачу, которая возвращается в другом потоке (например, вызов WCF) или отказывается от управления операционной системой (например, File IO), ожидание будет использовать меньше системных ресурсов, не блокируя поток.

Ответ 5

Wait() приведет к синхронному запуску потенциально асинхронного кода. ждать не будет.

Например, у вас есть веб-приложение asp.net. Пользовательские вызовы /getUser/ 1 конечная точка. Пул приложений asp.net выберет поток из пула потоков (Thread1), и этот поток сделает http-вызов. Если вы выполняете Wait(), этот поток будет заблокирован до разрешения http-вызова. Пока он ждет, если UserB вызывает /getUser/ 2, тогда для пула приложений потребуется еще один поток (Thread2). Вы только что создали (ну, на самом деле, из пула приложений) другой поток без причины, потому что вы не можете использовать Thread1, он был заблокирован Wait().

Если вы используете wait1 в Thread1, SyncContext будет управлять синхронизацией между вызовами Thread1 и http. Просто, он будет уведомлять, как только http-вызов будет выполнен. Между тем, если UserB вызывает /getUser/ 2, то вы снова будете использовать Thread1, чтобы сделать http-вызов, потому что он был выпущен после того, как его ожидают. Тогда другой запрос может использовать его еще больше. После завершения HTTP-вызова (user1 или user2) Thread1 может получить результат и вернуться к вызывающему абоненту (клиенту). Thread1 использовался для выполнения нескольких задач.

Ответ 6

В приведенном выше примере вы можете использовать "TaskCreationOptions.HideScheduler" и значительно изменить метод "DoAsTask". Сам метод не является асинхронным, как это происходит с "DoAsAsync", потому что он возвращает значение "Задача" и помечен как "асинхронный", делая несколько комбинаций, вот как он дает мне то же самое, что использовать "async/await",

static Task DoAsTask()
{
    WriteOutput("1 - Starting");
    var t = Task.Factory.StartNew<int>(DoSomethingThatTakesTime, TaskCreationOptions.HideScheduler); //<-- HideScheduler do the magic

    TaskCompletionSource<int> tsc = new TaskCompletionSource<int>();
    t.ContinueWith(tsk => tsc.TrySetResult(tsk.Result)); //<-- Set the result to the created Task

    WriteOutput("2 - Task started");

    tsc.Task.ContinueWith(tsk => WriteOutput("3 - Task completed with result: " + tsk.Result)); //<-- Complete the Task
    return tsc.Task;
}