What's the difference between Task.Start/Wait and Async/Await?
Asked Answered
C

6

221

I may be missing something but what is the difference between doing:

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);
}
Certitude answered 1/3, 2012 at 15:55 Comment(0)
L
418

I may be missing something

You are.

what is the difference between doing Task.Wait and await task?

You order your lunch from the waiter at the restaurant. A moment after giving your order, a friend walks in and sits down next to you and starts a conversation. Now you have two choices. You can ignore your friend until the task is complete -- you can wait until your soup arrives and do nothing else while you are waiting. Or you can respond to your friend, and when your friend stops talking, the waiter will bring you your soup.

Task.Wait blocks until the task is complete -- you ignore your friend until the task is complete. await keeps processing messages in the message queue, and when the task is complete, it enqueues a message that says "pick up where you left off after that await". You talk to your friend, and when there is a break in the conversation the soup arrives.

Lingam answered 1/3, 2012 at 16:4 Comment(21)
Isn't Task.Wait a cooperative block? i.e. it will not block the thread just pick a another task from the task pool and execute that while the timeout is still active.Laky
@Laky No, it's not. How would you like it if waiting for a Task that takes 10 ms would actually execute a 10 hour-long Task on your thread, thus blocking you for the whole 10 hours?Foresheet
@Eric Lippert .. so here lies my main concern. await keeps processing messages in the message queue and when the task is complete .... so does it mean await creates another thread and delegates it's task to it and in the mean time keeps on processing messages in message queue? But many says "await does not create a new thread" ? So who executes the task ?Hoax
@StrugglingCoder: The await operator doesn't do anything except evaluate its operand and then immediately return a task to the current caller. People get this idea in their heads that asynchrony can only be achieved through offloading work onto threads, but that's false. You can cook breakfast and read the paper while the toast is in the toaster without hiring a cook to watch the toaster. People say that well, there must be a thread -- a worker -- hidden inside the toaster, but I assure you that if you look in your toaster, there's no little guy in there watching the toast.Lingam
@StrugglingCoder: So, who is doing the work you ask? Maybe another thread is doing the work, and that thread has been assigned to a CPU, so the work is actually being done. Maybe the work is being done by hardware and there is no thread at all. But surely, you say, there must be some thread in the hardware. No. Hardware exists below the level of threads. There need be no thread! You might benefit from reading Stephen Cleary's article There Is No Thread.Lingam
@StrugglingCoder: Now, question, suppose there is asynchronous work being done and there is no hardware, and there is no other thread. How is this possible? Well, suppose the thing you awaited queues up a series of windows messages, each one of which does a little bit of work? Now what happens? You return control to the message loop, it starts pulling messages out of the queue, doing a little bit of work each time, and the last job that's done is "execute the continuation of the task". No extra thread!Lingam
@StrugglingCoder: Now, think about what I just said. You already know that this is how Windows works. You execute a series of mouse movements and button clicks and whatnot. Messages are queued up, processed in turn, each message causes a tiny amount of work to be done, and when its all done, the system keeps on going. Async on one thread is nothing more than what you're already used to: breaking up big tasks into little bits, queueing them up, and executing all the little bits in some order. Some of those executions cause other work to be queued up, and life goes on. One thread!Lingam
So what is the point of Task.Wait if it blocks? Why wrap code in a Task?Aerator
@IanWarburton: You should almost never use Task.Wait. You'd only use it if you absolutely have to block until the task is finished, and you know that it's not going to deadlock if you do.Lingam
is it different when you use it with Task.Run and Wait? because msdn says it is asynchronous msdn.microsoft.com/en-us/library/hh195051(v=vs.110).aspxSmitten
@batmaci: Is what different from what? If you want asynchrony then why on earth would you do a synchronous wait?Lingam
@batmaci: In the MSDN example, using Task.Run will run the specified Action in a new thread, so it is asynchronous. You end up with 2 threads: main thread and background/task thread. The difference with Task.Wait vs await is that Task.Wait will block on the main thread until the background thread finishes, where await would allow the main thread to continue executing other stuff. In the MSDN example, the Main method must wait for the task to complete, or else it would just return and the program would terminate before the task could finish.Uproar
The key in your answer (for me) was: The soup arrives when your friend stops talking. But I am still confused about one thing: Using async and await frees up resources (on the current thread) to execute some code in method x while method y (the awaited one) is still busy. That basically speeds up the execution of x. But what if I want y to run faster? In other words, what if I don't want to await the result but continue ahead. Is that where I need to spawn threads?Janiuszck
@NoelWidmer: Don't think about speeding up a task. Think about it as keeping a worker both busy and responsive. Tasks run as fast as they run; asynchrony is about making efficient use of workers. You only want to hire more workers if you have a job that can be divided among them easily. If it takes 20 minutes to bake a pie, hiring 20 cooks doesn't get you a pie in one minute.Lingam
@NoelWidmer: If what you want to express is "I don't care when this task completes; keep running this workflow", then simply do not await. The meaning of await is this is a position where this workflow cannot continue until this task is complete. If you think that await means "make this asynchronous" then you need to stop thinking that right now. Await means "find something else to do if this already-asynchronous job is not complete".Lingam
@NoelWidmer: In short: await is the sequencing operation on asynchronous workflows; it says what has to happen before what else has to happen. If that's not clear, this question might help: #47604175Lingam
For those who, like me, can't be fully comfortable without at least a basic explanation of where, at a mechanism level, the next code is coming from: Threads have a SynchronizationContext. For example, WindowsFormsSynchronizationContext executes messages in the message loop one at a time. Awaiting on the UI thread just means jumping to the next message in the message loop.Moth
@EricLippert If you look in your head while you read the newspaper, isn't there a little guy in there listening for the toast popping up?Absolutely
@EricLippert: I read at many locations that async-await can only help if long running operation is IO based; not the processor hungry. Why so? If I run synchronously, my program throughout the execution just utilizes 10% CPU. If I break my program in 2 small independent pieces and run the pieces asynchronously, it must finish execution in half time with 20% CPU usage. Please explain if I am still miss-understanding something.Silva
@AmitJoshi: async-await can certainly help with CPU-heavy operations, in two ways. First, it can help in the way you describe. Use the TPL to split the work into as many parts as there are CPUs, run the work in parallel, and await the results. If the work is highly parallelizable and more expensive than allocating a thread, this is a win. Second, you can keep all your work on the UI thread but put in task yields every 10 milliseconds or so. Your work will take longer, but the thread will be responsive to user input rather than hanging the UI.Lingam
@AmitJoshi: The important thing to remember is that async await is about coordinating workflows that contain high-latency operations. It does not matter whether the source of latency is IO or CPU; that's for the task provider to worry about.Lingam
C
126

To demonstrate Eric's answer here is some code:

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);
}
Certitude answered 1/3, 2012 at 16:30 Comment(2)
+1 for the code (it is better to run once than to read hundred times). But the phrase "//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!" is misleading. Upon pressing the button with t.Wait(); in button click event handler ButtonClick() it is not possible to press anything and then see something in console and update of label "until this task is complete" since the GUI is frozen and unresponsive, that is any clicks or interactions with GUI are being LOST until the completion of task waitingAmazon
I guess Eric assumes that you have a basic understanding of the Task api. I look at that code and say to myself "yup t.Wait is going to block on the main thread until the task is completed."Robbegrillet
R
54

This example demonstrates the difference very clearly. With async/await the calling thread will not block and continue executing.

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] 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 Output:

[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: Improved example by showing the thread ID in the output.

Ramshackle answered 29/9, 2015 at 10:30 Comment(6)
But if i do :new Task(DoAsTask).Start(); instead of DoAsAsync(); i get the same functionalety, so where is the benefit of await..Platitudinous
With your proposal, the result of the task must be evaluated somewhere else, maybe another method or a lambda. The async-await is makes the asynchronous code easier to follow. It's just a syntax enhancer.Ramshackle
@Ramshackle i don't get why Program End is after A - Started something. From my understanding when it comes to await keyword process should goes immediatly to main context and then go back.Muoimuon
@JimmyJimm From my understanding Task.Factory.StartNew will spin up a new thread to run DoSomethingThatTakesTime. As such there is no guarantee whether Program End or A - Started Something would be executed first.Ballad
@JimmyJimm: I have updated the sample to show the thread IDs. As you can see, "Program End" and "A - Started something" are running on different threads. So actually the order is not deterministic.Ramshackle
@Mas, I believe you have a bug in your code in that you should also call await on DoAsAsync - in which case they should both give the same output. Does this code compile? I would have thought that an async Main would also need to be used.Fumigator
L
14

Wait(), will cause to run potentially async code in sync manner. await will not.

For example, you have an asp.net web application. UserA calls /getUser/1 endpoint. asp.net app pool will pick a thread from thread pool (Thread1) and, this thread will make a http call. If you do Wait(), this thread will be blocked until http call resolves. While it is waiting, if UserB calls /getUser/2, then, app pool will need to serve another thread (Thread2) to make http call again. You just created (Well, fetched from app pool actually) another thread for no reason, because you cannot use Thread1 it was blocked by Wait().

If you use await on Thread1, then, SyncContext will manage sync between Thread1 and http call. Simply, it will notify once http call is done. Meanwhile, if UserB calls /getUser/2, then, you will use Thread1 again to make http call, because it was released once await got hit. Then another request can use it, even further more. Once http call is done (user1 or user2), Thread1 can get the result and return to caller (client). Thread1 was used for multiple tasks.

Lip answered 27/2, 2017 at 16:26 Comment(0)
H
10

In this example, not much, practically. If you are awaiting a Task that returns on a different thread (like a WCF call) or relinquishes control to the operating system (like File IO), await will use fewer system resources by not blocking a thread.

Ham answered 1/3, 2012 at 16:4 Comment(0)
D
3

In the example above, you can use "TaskCreationOptions.HideScheduler", and greatly modify the "DoAsTask" method. The method itself is not asynchronous, as it happens with "DoAsAsync" because it returns a "Task" value and is marked as "async", making several combinations, this is how it gives me exactly the same as using "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;
}
Diphase answered 31/8, 2017 at 23:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.