ChatGPT解决这个技术问题 Extra ChatGPT

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

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

E
Eric Lippert

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.


@ronag 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?
@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.
@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.
@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!
@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!
G
Gennady Vanin Геннадий Ванин

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

+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 waiting
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."
M
Mas

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.


But if i do :new Task(DoAsTask).Start(); instead of DoAsAsync(); i get the same functionalety, so where is the benefit of await..
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.
@Mas 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.
@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.
@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.
T
Teoman shipahi

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.


D
David Klempfner

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.


u
user8545699

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