В чем разница между заданием.Запуск / ожидание и асинхронность/ожидание?

возможно, я что-то упускаю, но в чем разница между этим:

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 ответов


возможно, я что-то упускаю

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

в чем разница между 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

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


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


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

например, у вас есть asp.net веб-приложение. Вызовы UserA / getUser / 1 конечная точка. asp.net пул приложений выберет поток из пула потоков (Thread1), и этот поток сделает http-вызов. Если вы выполните Wait (), этот поток будет заблокирован до разрешения 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;
}