简体   繁体   中英

Why is this async running asynchronously/threaded?

I'm following an article showing how async/tasks are not threads. https://blogs.msdn.microsoft.com/benwilli/2015/09/10/tasks-are-still-not-threads-and-async-is-not-parallel/

In their example they build up an async call showing this by creating 3 tasks that each run a loop that pauses for a number of seconds. I've recreated it in a console (marked as STAThread) application.

However, in my code below I was expecting it to take the same 15 seconds (5 seconds for each task) in both examples as stated in the article. However, it is only taking 5 seconds for the second and so is running all 3 at the same time resulting in 5 seconds to complete for my second example. The original article took 5 seconds but I changed it to a 1 second delay to make it more obvious.

Can anyone explain the what is going on and why it is running seemingly threaded?

    class AsyncTest
    {
        Stopwatch sw = new Stopwatch();
        internal void Tests()
        {
            DoASynchronous1();
            //update below to be DoAsync2 and repeat
        }

        private void DoASynchronous1()
        {
            sw.Restart();
            var start = sw.ElapsedMilliseconds;
            Console.WriteLine($"\nStarting ASync Test, interval 1000ms");
            Task a=DoASync1("A",1000);     //change these to 2           
            Task b=DoASync1("B",1000);                
            Task c=DoASync1("C",1000);                
            Task.WaitAll(a, b, c);
            sw.Stop();
            Console.WriteLine($"Ended Sync Test. Took {(sw.ElapsedMilliseconds - start)} mseconds");
            Console.ReadKey();
        }

        //this runs synchronously showing async is not threaded
        //this takes 15 seconds
        private async Task DoASync1(string v, int delay)
        {
            //loop for 5 seconds
            for (int i = 1; i <= 5; i++)
            {
                await Task.Delay(0); //or simply omit
                var startTime = sw.ElapsedMilliseconds;
                while (sw.ElapsedMilliseconds < startTime+(delay)) { }
                Console.WriteLine($"{v}:{i}");
            }
        }

        //this is taking 5 seconds
        private async Task DoASync2(string v, int delay)
        {
            //loop for 5 seconds
            for (int i = 1; i <= 5; i++)
            {
                await Task.Delay(100);
                var startTime = sw.ElapsedMilliseconds;
                while (sw.ElapsedMilliseconds < startTime + delay) { }
                var endtime = sw.ElapsedMilliseconds;
                Console.WriteLine($"{v}:{endtime}");
            }
        }

    }
}

The key here is the absence of a synchronisation context. The linked article is a WPF application, which has a synchronisation context on its Main/UI thread. The console app you've created won't have one at all.

The result of this is that where the article says eg

When you “await” an asynchronous task, the rest of your method will continue running in the same context that it started on. In WPF, that context is the UI thread.

That doesn't apply to your example. After Task.Delay , your code will resume on a thread pool thread. This allows all 3 of your tasks to run in parallel.

This is much the same as when the article later uses ConfigureAwait(false) , which prevents resuming execution on the captured synchronisation context.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM