В чем разница между задачами.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);
}
6 184

6 ответов:

Я могу что-то упустить

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

в чем разница между Task.Wait и await task?

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

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

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

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);
}

этот пример демонстрирует разницу очень ясно. С 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

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

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

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

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

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

в приведенном выше примере можно использовать "TaskCreationOptions.HideScheduler", и значительно изменить метод" DoAsTask". Сам метод не является асинхронным, как это происходит с "DoAsAsync", потому что он возвращает значение "Task" и помечается как "async", делая несколько комбинаций, вот как это дает мне точно так же, как с помощью "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;
}