How and when to use ‘async’ and ‘await’

前端 未结 21 1790
你的背包
你的背包 2020-11-21 05:07

From my understanding one of the main things that async and await do is to make code easy to write and read - but is using them equal to spawning background threads to perfo

相关标签:
21条回答
  • 2020-11-21 05:22

    From my understanding one of the main things that async and await do is to make code easy to write and read.

    They're to make asynchronous code easy to write and read, yes.

    Is it the same thing as spawning background threads to perform long duration logic?

    Not at all.

    // I don't understand why this method must be marked as 'async'.

    The async keyword enables the await keyword. So any method using await must be marked async.

    // This line is reached after the 5 seconds sleep from DoSomethingAsync() method. Shouldn't it be reached immediately?

    No, because async methods are not run on another thread by default.

    // Is this executed on a background thread?

    No.


    You may find my async/await intro helpful. The official MSDN docs are also unusually good (particularly the TAP section), and the async team put out an excellent FAQ.

    0 讨论(0)
  • 2020-11-21 05:23
    public static void Main(string[] args)
    {
        string result = DownloadContentAsync().Result;
        Console.ReadKey();
    }
    
    // You use the async keyword to mark a method for asynchronous operations.
    // The "async" modifier simply starts synchronously the current thread. 
    // What it does is enable the method to be split into multiple pieces.
    // The boundaries of these pieces are marked with the await keyword.
    public static async Task<string> DownloadContentAsync()// By convention, the method name ends with "Async
    {
        using (HttpClient client = new HttpClient())
        {
            // When you use the await keyword, the compiler generates the code that checks if the asynchronous operation is finished.
            // If it is already finished, the method continues to run synchronously.
            // If not completed, the state machine will connect a continuation method that must be executed WHEN the Task is completed.
    
    
            // Http request example. 
            // (In this example I can set the milliseconds after "sleep=")
            String result = await client.GetStringAsync("http://httpstat.us/200?sleep=1000");
    
            Console.WriteLine(result);
    
            // After completing the result response, the state machine will continue to synchronously execute the other processes.
    
    
            return result;
        }
    }
    
    0 讨论(0)
  • 2020-11-21 05:27

    When using async and await the compiler generates a state machine in the background.

    Here's an example on which I hope I can explain some of the high-level details that are going on:

    public async Task MyMethodAsync()
    {
        Task<int> longRunningTask = LongRunningOperationAsync();
        // independent work which doesn't need the result of LongRunningOperationAsync can be done here
    
        //and now we call await on the task 
        int result = await longRunningTask;
        //use the result 
        Console.WriteLine(result);
    }
    
    public async Task<int> LongRunningOperationAsync() // assume we return an int from this long running operation 
    {
        await Task.Delay(1000); // 1 second delay
        return 1;
    }
    

    OK, so what happens here:

    1. Task<int> longRunningTask = LongRunningOperationAsync(); starts executing LongRunningOperation

    2. Independent work is done on let's assume the Main Thread (Thread ID = 1) then await longRunningTask is reached.

      Now, if the longRunningTask hasn't finished and it is still running, MyMethodAsync() will return to its calling method, thus the main thread doesn't get blocked. When the longRunningTask is done then a thread from the ThreadPool (can be any thread) will return to MyMethodAsync() in its previous context and continue execution (in this case printing the result to the console).

    A second case would be that the longRunningTask has already finished its execution and the result is available. When reaching the await longRunningTask we already have the result so the code will continue executing on the very same thread. (in this case printing result to console). Of course this is not the case for the above example, where there's a Task.Delay(1000) involved.

    0 讨论(0)
  • 2020-11-21 05:27

    Further to the other answers, have a look at await (C# Reference)

    and more specifically at the example included, it explains your situation a bit

    The following Windows Forms example illustrates the use of await in an async method, WaitAsynchronouslyAsync. Contrast the behavior of that method with the behavior of WaitSynchronously. Without an await operator applied to a task, WaitSynchronously runs synchronously despite the use of the async modifier in its definition and a call to Thread.Sleep in its body.

    private async void button1_Click(object sender, EventArgs e)
    {
        // Call the method that runs asynchronously.
        string result = await WaitAsynchronouslyAsync();
    
        // Call the method that runs synchronously.
        //string result = await WaitSynchronously ();
    
        // Display the result.
        textBox1.Text += result;
    }
    
    // The following method runs asynchronously. The UI thread is not
    // blocked during the delay. You can move or resize the Form1 window 
    // while Task.Delay is running.
    public async Task<string> WaitAsynchronouslyAsync()
    {
        await Task.Delay(10000);
        return "Finished";
    }
    
    // The following method runs synchronously, despite the use of async.
    // You cannot move or resize the Form1 window while Thread.Sleep
    // is running because the UI thread is blocked.
    public async Task<string> WaitSynchronously()
    {
        // Add a using directive for System.Threading.
        Thread.Sleep(10000);
        return "Finished";
    }
    
    0 讨论(0)
  • 2020-11-21 05:27

    Async / Await

    Actually Async / Await are a pair of keywords which are just syntactic sugar for creating a callback of an asynchronous task.

    Take by example this operation:

        public static void DoSomeWork()
        {
            var task = Task.Run(() =>
            {
                // [RUNS ON WORKER THREAD]
    
                // IS NOT bubbling up due to the different threads
                throw new Exception();
                Thread.Sleep(2000);
    
                return "Hello";
            });
    
            // This is the callback
            task.ContinueWith((t) => {
                // -> Exception is swallowed silently
                Console.WriteLine("Completed");
    
                // [RUNS ON WORKER THREAD]
            });
        }
    

    The code above has several disadvantages. Errors are not passed on and it's hard to read. But Async and Await come in to help us out:

        public async static void DoSomeWork()
        {
            var result = await Task.Run(() =>
            {
                // [RUNS ON WORKER THREAD]
    
                // IS bubbling up
                throw new Exception();
                Thread.Sleep(2000);
    
                return "Hello";
            });
    
            // every thing below is a callback 
            // (including the calling methods)
    
            Console.WriteLine("Completed");
    
        }
    

    Await calls have to be in Async methods. This has some advantages:

    • Returns the result of the Task
    • creates automatically a callback
    • checks for errors and lets them bubble up in callstack (only up to none-await calls in callstack)
    • waits for the result
    • frees up the main thread
    • runs the callback on the main thread
    • uses a worker thread from the threadpool for the task
    • makes the code easy to read
    • and a lot more

    NOTE: Async and Await are used with asynchronous calls not to make these. You have to use Task Libary for this, like Task.Run() .

    Here is a comparison between await and none await solutions

    This is the none async solution:

        public static long DoTask()
        {
            stopWatch.Reset();
            stopWatch.Start();
    
            // [RUNS ON MAIN THREAD]
            var task = Task.Run(() => {
                Thread.Sleep(2000);
                // [RUNS ON WORKER THREAD]
            });
            // goes directly further
            // WITHOUT waiting until the task is finished
    
            // [RUNS ON MAIN THREAD]
    
            stopWatch.Stop();
            // 50 milliseconds
            return stopWatch.ElapsedMilliseconds;
        }
    

    This is the async method:

        public async static Task<long> DoAwaitTask()
        {
            stopWatch.Reset();
            stopWatch.Start();
    
            // [RUNS ON MAIN THREAD]
    
            await Task.Run(() => {
                Thread.Sleep(2000);
                // [RUNS ON WORKER THREAD]
            });
            // Waits until task is finished
    
            // [RUNS ON MAIN THREAD]
    
            stopWatch.Stop();
            // 2050 milliseconds
            return stopWatch.ElapsedMilliseconds;
        }
    

    You can actually call an async method without the await keyword but this means that any Exception here are swallowed in release mode:

        public static Stopwatch stopWatch { get; } = new Stopwatch();
    
        static void Main(string[] args)
        {
            Console.WriteLine("DoAwaitTask: " + DoAwaitTask().Result + " ms");
            // 2050 (2000 more because of the await)
            Console.WriteLine("DoTask: " + DoTask() + " ms");
            // 50
            Console.ReadKey();
        }
    

    Async and Await are not meant for parallel computing. They are used to not block your main thread. When it's about asp.net or Windows applications, blocking your main thread due to a network call is a bad thing. If you do this, your app will get unresponsive or even crash.

    Check out ms docs for more examples.

    0 讨论(0)
  • 2020-11-21 05:30

    To be honest I still think the best explanation is the one about future and promises on the Wikipedia: http://en.wikipedia.org/wiki/Futures_and_promises

    The basic idea is that you have a separate pool of threads that execute tasks asynchronously. When using it. The object does however make the promise that it will execute the operation at some time and give you the result when you request it. This means that it will block when you request the result and hasn't finished, but execute in the thread pool otherwise.

    From there you can optimize things: some operations can be implemented async and you can optimize things like file IO and network communication by batching together subsequent requests and/or reordering them. I'm not sure if this is already in the task framework of Microsoft - but if it isn't that would be one of the first things I would add.

    You can actually implement the future pattern sort-of with yields in C# 4.0. If you want to know how it works exactly, I can recommend this link that does a decent job: http://code.google.com/p/fracture/source/browse/trunk/Squared/TaskLib/ . However, if you start toying with it yourself, you will notice that you really need language support if you want to do all the cool things -- which is exactly what Microsoft did.

    0 讨论(0)
提交回复
热议问题