it-swarm.com.ru

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

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

Вы.

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

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

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

383
Eric Lippert

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

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

Этот пример демонстрирует разницу очень четко. При использовании 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 Output:

 [1] Начало программы 
 [1] 1 - Запуск 
 [1] 2 - Задание запущено 
 [3] A - Началось что-то 
 [3] B - Что-то выполнено 
 [1] 3 - Задание выполнено с результатом: 123 
 [1] Конец программы 

Результат DoAsAsync:

 [1] Начало программы 
 [1] 1 - Запуск 
 [1] 2 - Задание запущено 
 [3] A - Началось что-то 
 [1] Конец программы 
 [3] B - Что-то выполнено 
 [3] 3 - Задание выполнено с результатом: 123 

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

44
Mas

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

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

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

9
Teoman shipahi

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

8
foson

В приведенном выше примере вы можете использовать "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;
}
3
user8545699