## 使用C# 6.0中的async/await


异步函数是TPL之上更高级别的抽象,真正简化了异步编程,它与普通函数不一样在于必须有async标识,并且返回类型一般是Task,Task类型,当然也可以使用async void,但更推荐使用async Task,使用async void唯一合理的地方在于程序中使用顶层UI控制器事件处理器的时候。

异步函数内部,必须至少有一个await操作符,该操作符一般与TPL一起工作,并获取该任务中的异步操作的结果。需要注意的是在执行完await调用的代码行后该方法会立即返回,剩下的异步函数的部分独立异步运行,值得注意的是如果程序中有两个连续的await操作符,它们是顺序运行的,即第一个完成后,第二个才会开始运行。

await关键字简化了连续性的附着(Simplify the attaching of continuations)。

比如:

var result=await expression;
statement(s);

等价于:

var awaiter=expression.GetAwaiter();
awaiter.OnCompleted(()=>{
    var result=awaiter.GetResult();
    statement(s);
});

可以用以下demo佐证:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ThreadDemo
{
    class Program21
    {
        static async Task GetInfoAsync(string name,int seconds)
        {
            //await Task.Delay(TimeSpan.FromSeconds(seconds));
            await Task.Run(() => { 
                Thread.Sleep(TimeSpan.FromSeconds(seconds));
                Console.WriteLine($"{name} first :{Thread.CurrentThread.ManagedThreadId}");
            });
            await Task.Run(() => {
                Thread.Sleep(TimeSpan.FromSeconds(seconds));
                Console.WriteLine($"{name} second:{Thread.CurrentThread.ManagedThreadId}");
            });
            return $"Task {name} is running on a thread id " +
                $"{Thread.CurrentThread.ManagedThreadId}. " +
                $"Is thread pool thread:{Thread.CurrentThread.IsThreadPoolThread}";
        }
        static async Task AsyncProcess()
        {
            Task t1 = GetInfoAsync("Task 1", 3);
            Task t2 = GetInfoAsync("Task 2", 5);
            string[] results = await Task.WhenAll(t1, t2);
            foreach(string result in results)
            {
                Console.WriteLine(result);
            }
        }
        static void Main()
        {
            Task t = AsyncProcess();
            t.Wait();
        }
    }
}

output:

Task 1 first :3
Task 2 first :4
Task 1 second:5
Task 2 second:3
Task Task 1 is running on a thread id 5. Is thread pool thread:True
Task Task 2 is running on a thread id 3. Is thread pool thread:True

这也说明了为什么上述GetInfoAsync返回的是Task类型,因为await关键字后的任务后面的statement,实际上就是在最后一个await的任务的线程里完成的,所以return $"Task {name} is running on a thread id " + $"{Thread.CurrentThread.ManagedThreadId}. " + $"Is thread pool thread:{Thread.CurrentThread.IsThreadPoolThread}";自然是异步中返回string的函数,即是Task类型。

await最大的优势在于可以出现在异步函数中的任意地方(除了lock,unsafe环境)。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ThreadDemo
{
    class Program22
    {
        static async Task GetInoAsync(string name,int seconds)
        {
            Console.WriteLine($"GetInfoAsync Pre:{Thread.CurrentThread.ManagedThreadId}");
            await Task.Delay(TimeSpan.FromSeconds(seconds));
            Console.WriteLine($"GetInfoAsync After:{Thread.CurrentThread.ManagedThreadId}");
            //throw new Exception($"Boom from {name}!");
            return $"In {Thread.CurrentThread.ManagedThreadId}";
        }
        static async Task AsyncProcess()
        {
            Console.WriteLine("1. Single exception");
            try
            {
                Console.WriteLine($"AsyncProcess Pre:{Thread.CurrentThread.ManagedThreadId}");
                string result = await GetInoAsync("Task 1", 2);
                Console.WriteLine(result);
                Console.WriteLine($"AsyncProcess after:{Thread.CurrentThread.ManagedThreadId}");
            }
            catch(Exception ex)
            {
                Console.WriteLine($"Exception details:{ex}");
            }
        }
        static void Main()
        {
            Console.WriteLine($"Main ID:{Thread.CurrentThread.ManagedThreadId}");
            Task t = AsyncProcess();
            t.Wait();
        }
    }
}

output:

Main ID:1
1. Single exception
AsyncProcess Pre:1
GetInfoAsync Pre:1
GetInfoAsync After:4
In 4
AsyncProcess after:4

上面的demo,清楚的显示了线程随着await的变化。

使用await操作符获取异步任务结果

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ThreadDemo
{
    class Program17
    {
        static async Task GetInfoAsync(string name)
        {
            await Task.Delay(TimeSpan.FromSeconds(2));
            return $"Task {name} is running on a thread id {Thread.CurrentThread.ManagedThreadId}" +
                $" Is thread pool thread: {Thread.CurrentThread.IsThreadPoolThread}";
        }

        static Task AsynchronyWithTPL()
        {
            Task t = GetInfoAsync("Task 1");
            Task t2 = t.ContinueWith(task => Console.WriteLine(t.Result), TaskContinuationOptions.NotOnFaulted);
            Task t3 = t.ContinueWith(task => Console.WriteLine(t.Exception.InnerException), TaskContinuationOptions.OnlyOnFaulted);
            return Task.WhenAny(t2, t3);
        }
        static async Task AsynchronyWithAwait()
        {
            try
            {
                string result = await GetInfoAsync("Task 2");
                Console.WriteLine(result);
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex);
            }
        }
        static void Main()
        {
            Task t = AsynchronyWithTPL();
            t.Wait();

            t = AsynchronyWithAwait();
            t.Wait();
        }
    }
}

outputt:

Task Task 1 is running on a thread id 4 Is thread pool thread: True
Task Task 2 is running on a thread id 6 Is thread pool thread: True

如果将Task GetInfoAsync函数改为:

        static async Task GetInfoAsync(string name)
        {
            await Task.Delay(TimeSpan.FromSeconds(2));
            //return $"Task {name} is running on a thread id {Thread.CurrentThread.ManagedThreadId}" +
            //    $" Is thread pool thread: {Thread.CurrentThread.IsThreadPoolThread}";
            throw new Exception("Boom!");
        }

output:

System.Exception: Boom!
   在 ThreadDemo.Program17.d__0.MoveNext() 位置 E:\LearningCSharp\ThreadTestNetFramework\ThreadDemo\Program17.cs:行号 16
System.Exception: Boom!
   在 ThreadDemo.Program17.d__0.MoveNext() 位置 E:\LearningCSharp\ThreadTestNetFramework\ThreadDemo\Program17.cs:行号 16
--- 引发异常的上一位置中堆栈跟踪的末尾 ---
   在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   在 ThreadDemo.Program17.d__2.MoveNext() 位置 E:\LearningCSharp\ThreadTestNetFramework\ThreadDemo\Program17.cs:行号 30
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ThreadDemo
{
    class Program17
    {
        static async Task GetInfoAsync(string name)
        {
            await Task.Run(() => { Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId} is running.."); });
            await Task.Delay(TimeSpan.FromSeconds(2));
            return $"Task {name} is running on a thread id {Thread.CurrentThread.ManagedThreadId}" +
                $" Is thread pool thread: {Thread.CurrentThread.IsThreadPoolThread}";
            //throw new Exception("Boom!");
        }

        static Task AsynchronyWithTPL()
        {
            Task t = GetInfoAsync("Task 1");
            Task t2 = t.ContinueWith(task => Console.WriteLine(t.Result), TaskContinuationOptions.NotOnFaulted);
            Task t3 = t.ContinueWith(task => Console.WriteLine(t.Exception.InnerException), TaskContinuationOptions.OnlyOnFaulted);
            return Task.WhenAny(t2, t3);
        }
        static async Task AsynchronyWithAwait()
        {
            try
            {
                string result = await GetInfoAsync("Task 2");
                Console.WriteLine(result);
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex);
            }
        }
        static void Main()
        {
            Task t = AsynchronyWithTPL();
            t.Wait();

            t = AsynchronyWithAwait();
            t.Wait();
        }
    }
}

output:

3 is running..
Task Task 1 is running on a thread id 4 Is thread pool thread: True
3 is running..
Task Task 2 is running on a thread id 6 Is thread pool thread: True

可以发现同一个异步函数内部,不同的await后的任务处理确实是在不同的线程池。

在lambda表达式中使用await操作符

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ThreadDemo
{
    class program18
    {
        static async Task AsynchronousProcessing()
        {
            Func> asyncLambda = async name =>
               {
                   await Task.Delay(TimeSpan.FromSeconds(2));
                   return $"Task {name} is running on a thread id {Thread.CurrentThread.ManagedThreadId}." +
                   $" Is thread pool thread:{Thread.CurrentThread.IsThreadPoolThread}";
               };
            string result = await asyncLambda("async lambda");
            Console.WriteLine(result);
        }
        static void Main()
        {
            Task t = AsynchronousProcessing();
            t.Wait();
        }
    }
}

output:

Task async lambda is running on a thread id 4. Is thread pool thread:True

对连续的异步任务使用await操作符

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ThreadDemo
{
    class program19
    {
        static async Task GetInfoAsync(string name)
        {
            Console.WriteLine($"Task {name} started!");
            await Task.Delay(TimeSpan.FromSeconds(2));
            if(name=="TPL 2")
            {
                throw new Exception("Boom!");
            }
            return $"Task {name} is running on a thread id {Thread.CurrentThread.ManagedThreadId}." +
                $" Is thread pool thread:{Thread.CurrentThread.IsThreadPoolThread}";
        }

        static async Task AsyncronyWithAwait()
        {
            try
            {
                string result = await GetInfoAsync("Async 1");
                Console.WriteLine(result);
                result = await GetInfoAsync("Async 2");
                Console.WriteLine(result);
            }
            catch(Exception ex)
            {
                Console.WriteLine(ex);
            }
        }

        static void Main()
        {
            Task t = AsyncronyWithAwait();
            t.Wait();
        }
    }
}

output:

Task Async 1 started!
Task Async 1 is running on a thread id 4. Is thread pool thread:True
Task Async 2 started!
Task Async 2 is running on a thread id 4. Is thread pool thread:True

对并行执行的异步任务使用await操作符

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ThreadDemo
{
    class Program21
    {
        static async Task GetInfoAsync(string name,int seconds)
        {
            await Task.Delay(TimeSpan.FromSeconds(seconds));
            //await Task.Run(() => Thread.Sleep(TimeSpan.FromSeconds(seconds)));
            return $"Task {name} is running on a thread id " +
                $"{Thread.CurrentThread.ManagedThreadId}. " +
                $"Is thread pool thread:{Thread.CurrentThread.IsThreadPoolThread}";
        }
        static async Task AsyncProcess()
        {
            Task t1 = GetInfoAsync("Task 1", 3);
            Task t2 = GetInfoAsync("Task 2", 5);
            string[] results = await Task.WhenAll(t1, t2);
            foreach(string result in results)
            {
                Console.WriteLine(result);
            }
        }
        static void Main()
        {
            Task t = AsyncProcess();
            t.Wait();
        }
    }
}

output:

Task Task 1 is running on a thread id 4. Is thread pool thread:True
Task Task 2 is running on a thread id 4. Is thread pool thread:True

我们使用了Task.WhenAll辅助方法创建了另一个任务,该任务的返回类型是Task,该任务只有在所有底层任务完成后才会运行。5s后,我们可以观察到,一瞬间全部出来了结果,这说明了这个任务在t1,t2都结束后,才执行。

但这里也观察到一个非常有趣的现象:并行任务是被线程池同一个工作者线程执行的,How can it be?

为了搞清楚这个问题,我们需要知道Task.Delay的工作机理是怎么样的!

它是使用类似这样的技术实现的:

Task Delay(int milliseconds)
{
   var tcs=new TaskCompletionSource();
    var timer=new System.Timers.Timer(milliseconds){AutoReset=false};//保证只调用一次
    timer.Elapsed+=delegate{timer.Dispose();tcs.SetResult(null);};
    timer.Start();
    return tcs.Task;
}

由此可见,对于GetInfoAsync异步方法,当timer所在的线程A达到milliseconds后,坍缩,由于await关键字,线程A将继续执行return 语句,然后A结束,重新回到线程池,当t2再运行时,那么就很有可能重新从线程池中拾取到线程A,结果就造成了t1,t2都用了同一个worker thread。

如果使用await Task.Run(() => { Thread.Sleep(TimeSpan.FromSeconds(seconds)); 就肯定不会出现以上重复使用同一个worker thread的情况。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ThreadDemo
{
    class Program21
    {
        static async Task GetInfoAsync(string name,int seconds)
        {
            //await Task.Delay(TimeSpan.FromSeconds(seconds));
            await Task.Run(() => { 
                Thread.Sleep(TimeSpan.FromSeconds(seconds));
                Console.WriteLine($"{name} first :{Thread.CurrentThread.ManagedThreadId}");
            });
            await Task.Run(() => {
                Thread.Sleep(TimeSpan.FromSeconds(seconds));
                Console.WriteLine($"{name} second:{Thread.CurrentThread.ManagedThreadId}");
            });
            return $"Task {name} is running on a thread id " +
                $"{Thread.CurrentThread.ManagedThreadId}. " +
                $"Is thread pool thread:{Thread.CurrentThread.IsThreadPoolThread}";
        }
        static async Task AsyncProcess()
        {
            Task t1 = GetInfoAsync("Task 1", 3);
            Task t2 = GetInfoAsync("Task 2", 5);
            string[] results = await Task.WhenAll(t1, t2);
            foreach(string result in results)
            {
                Console.WriteLine(result);
            }
        }
        static void Main()
        {
            Task t = AsyncProcess();
            t.Wait();
        }
    }
}

output:

Task 1 first :3
Task 2 first :4
Task 1 second:5
Task 2 second:3
Task Task 1 is running on a thread id 5. Is thread pool thread:True
Task Task 2 is running on a thread id 3. Is thread pool thread:True

处理异步操作中的异常

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ThreadDemo
{
    class Program22
    {
        static async Task GetInoAsync(string name,int seconds)
        {
            Console.WriteLine($"GetInfoAsync Pre:{Thread.CurrentThread.ManagedThreadId}");
            await Task.Delay(TimeSpan.FromSeconds(seconds));
            Console.WriteLine($"GetInfoAsync After:{Thread.CurrentThread.ManagedThreadId}");
            throw new Exception($"Boom from {name}!");
            //return $"In {Thread.CurrentThread.ManagedThreadId}";
        }
        static async Task AsyncProcess()
        {
            Console.WriteLine("1. Single exception");
            try
            {
                Console.WriteLine($"AsyncProcess Pre:{Thread.CurrentThread.ManagedThreadId}");
                string result = await GetInoAsync("Task 1", 2);
                Console.WriteLine(result);
                Console.WriteLine($"AsyncProcess after:{Thread.CurrentThread.ManagedThreadId}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Exception details:{ex}");
            }
            Console.WriteLine();
            Console.WriteLine("2. Multiple exception");
            Console.WriteLine($"Multiple exception Pre:{Thread.CurrentThread.ManagedThreadId}");
            Task t1 = GetInoAsync("Task 1", 3);
            Task t2 = GetInoAsync("Task 2", 2);
            Console.WriteLine($"Multiple await Pre:{Thread.CurrentThread.ManagedThreadId}");
            try
            {
                string[] results = await Task.WhenAll(t1, t2);
                Console.WriteLine(results.Length);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Exception details:{ex}");
            }

            Console.WriteLine();
            Console.WriteLine("3. Multiple exceptions witth AggregateExceptioon");
            t1 = GetInoAsync("Task 1", 3);
            t2 = GetInoAsync("Task 2", 2);
            Task t3 = Task.WhenAll(t1, t2);
            try
            {
                string[] results = await t3;
                Console.WriteLine(results.Length);
            }
            catch
            {
                var ae = t3.Exception.Flatten();
                var exceptions = ae.InnerExceptions;
                Console.WriteLine($"Exceptions caught:{exceptions.Count}");
                foreach (var e in exceptions)
                {
                    Console.WriteLine($"Exception details:{e}");
                    Console.WriteLine();
                }
            }
            Console.WriteLine();
            Console.WriteLine("4.await in catch and finally blocks");
            try
            {
                string result = await GetInoAsync("Task 1", 2);
                Console.WriteLine(result);
            }
            catch(Exception ex)
            {
                await Task.Delay(TimeSpan.FromSeconds(1));
                Console.WriteLine($"Catch block with await:Exceptioon details:{ex}");
            }
            finally
            {
                await Task.Delay(TimeSpan.FromSeconds(1));
                Console.WriteLine("Finally block");
            }
        }
        static void Main()
        {
            Console.WriteLine($"Main ID:{Thread.CurrentThread.ManagedThreadId}");
            Task t = AsyncProcess();
            t.Wait();
        }
    }
}

output:

Main ID:1
1. Single exception
AsyncProcess Pre:1
GetInfoAsync Pre:1
GetInfoAsync After:4
Exception details:System.Exception: Boom from Task 1!
   在 ThreadDemo.Program22.d__0.MoveNext() 位置 E:\LearningCSharp\ThreadTestNetFramework\ThreadDemo\Program22.cs:行号 16
--- 引发异常的上一位置中堆栈跟踪的末尾 ---
   在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   在 ThreadDemo.Program22.d__1.MoveNext() 位置 E:\LearningCSharp\ThreadTestNetFramework\ThreadDemo\Program22.cs:行号 25

2. Multiple exception
Multiple exception Pre:4
GetInfoAsync Pre:4
GetInfoAsync Pre:4
Multiple await Pre:4
GetInfoAsync After:4
GetInfoAsync After:5
Exception details:System.Exception: Boom from Task 1!
   在 ThreadDemo.Program22.d__0.MoveNext() 位置 E:\LearningCSharp\ThreadTestNetFramework\ThreadDemo\Program22.cs:行号 16
--- 引发异常的上一位置中堆栈跟踪的末尾 ---
   在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   在 ThreadDemo.Program22.d__1.MoveNext() 位置 E:\LearningCSharp\ThreadTestNetFramework\ThreadDemo\Program22.cs:行号 41

3. Multiple exceptions witth AggregateExceptioon
GetInfoAsync Pre:5
GetInfoAsync Pre:5
GetInfoAsync After:4
GetInfoAsync After:5
Exceptions caught:2
Exception details:System.Exception: Boom from Task 1!
   在 ThreadDemo.Program22.d__0.MoveNext() 位置 E:\LearningCSharp\ThreadTestNetFramework\ThreadDemo\Program22.cs:行号 16
--- 引发异常的上一位置中堆栈跟踪的末尾 ---
   在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   在 ThreadDemo.Program22.d__1.MoveNext() 位置 E:\LearningCSharp\ThreadTestNetFramework\ThreadDemo\Program22.cs:行号 56

Exception details:System.Exception: Boom from Task 2!
   在 ThreadDemo.Program22.d__0.MoveNext() 位置 E:\LearningCSharp\ThreadTestNetFramework\ThreadDemo\Program22.cs:行号 16


4.await in catch and finally blocks
GetInfoAsync Pre:5
GetInfoAsync After:4
Catch block with await:Exceptioon details:System.Exception: Boom from Task 1!
   在 ThreadDemo.Program22.d__0.MoveNext() 位置 E:\LearningCSharp\ThreadTestNetFramework\ThreadDemo\Program22.cs:行号 16
--- 引发异常的上一位置中堆栈跟踪的末尾 ---
   在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   在 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   在 ThreadDemo.Program22.d__1.MoveNext() 位置 E:\LearningCSharp\ThreadTestNetFramework\ThreadDemo\Program22.cs:行号 74
Finally block

一个常见的错误就是,对一个以上的异步操作使用await时还使用以上方式。但如果仍像第一种情况一样使用catch代码块,则只能从底层的AggregatetException对象中得到第一个异常,正如第二种情况表现的那样。

为了收集所有异常信息,**可以使用await任务的Exception属性,第三种情况,使用了AggreagateExceptionFlatten方法将层次异常放入一个列表,并从中提取出所有的底层异常。

避免使用捕获的同步上下文

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
namespace ThreadDemo
{
    class Program23
    {
        private static Label _label;
        static async void Click(object sender,EventArgs e)
        {
            _label.Content = new TextBlock() { Text = "Calculating..." };
            TimeSpan resultWithContext = await Test();
            TimeSpan resultNoContext = await TestNoContext();
            //TimeSpan resultNoContext = await TestNoContext().ConfigureAwait(false);
            StringBuilder sb = new StringBuilder();
            sb.AppendLine($"With the context:{resultWithContext}");
            sb.AppendLine($"Without the context:{resultNoContext}");
            sb.AppendLine("Ratio:" +
                $"{resultWithContext.TotalMilliseconds / resultNoContext.TotalMilliseconds:0.00}");
            _label.Content = new TextBlock() { Text = sb.ToString() };
        }
        [STAThread]
        static void Main()
        {
            Console.WriteLine($"Main Thread ID:{Thread.CurrentThread.ManagedThreadId}");
            Application app = new Application();
            Window win = new Window();
            StackPanel panel = new StackPanel();
            Button button = new Button();
            _label = new Label();
            _label.FontSize = 32;
            _label.Height = 200;
            button.Height = 200;
            button.FontSize = 32;
            button.Content = new TextBlock() { Text = "Start async operations" };
            button.Click += Click;
            panel.Children.Add(_label);
            panel.Children.Add(button);
            win.Content = panel;
            app.Run(win);
            Console.ReadLine();
        }
        static async Task Test()
        {
            const int iterationsNumber = 100000;
            var sw = new Stopwatch();
            sw.Start();
            for(int i = 0; i < iterationsNumber; i++)
            {
                var t = Task.Run(() =>{ });
                await t;
            }
            sw.Stop();
            return sw.Elapsed;
        }
        static async Task TestNoContext()
        {
            const int iterationsNumber = 100000;
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for(int i = 0; i < iterationsNumber; i++)
            {
                Task t = Task.Run(() => { });
                await t.ConfigureAwait(continueOnCapturedContext: false);
            }
            sw.Stop();
            return sw.Elapsed;
        }
    }
}

可以看到常规的await操作符花费了更多的时间来完成,这是因为向UI线程中放入了成百上千个后续操作任务,这会使用它的消息循环来异步地执行这些任务,在本例中,我们无需再UI线程中运行该代码,因为异步操作并未访问UI组件

....未完待续

C