C# 异步编程async/await详解

异步编程async/await详解

1.关键字async

当函数使用async标记后,返回值必须为void,Task,Task<T>,当返回值为Task<T>时,函数内部只需要返回T类型,编译器会自动包装成Task<T>类型,如下两个函数执行结果一致:

        public Task<int> F1()

        {

            return Task.FromResult(5);

        }



        public async Task<int> F2()

        {

            return 5;

        }

2.关键字await

await关键字必须在具有async标记的函数内使用,使用await关键字后编译器会使用状态机进行编译,await关键字不仅仅只可以对Tsak对象使用,我们也可以自己实现await的对象。当函数使用了一个await关键字后,会被分割成两部分,编译的时候会当作函数的两个状态。函数的两部分会分别是await前面的一部分,和await后面的一部分,例如:

这两部分运行的位置不一样,第一部分和正常函数一样调用函数的时候直接执行,第二部分的执行取决于我们await的对象,执行的位置也在await对象内部。下面是一个await对象的实现,展示了各个部分的执行过程。

    public class A
    {
        private B b = new B();
        public B GetAwaiter()
        {
            Console.WriteLine("A.GetAwaiter");
            return b;
        }
    }

    public class B : INotifyCompletion
    {
        private bool isCompleted = false;
        public bool IsCompleted
        {
            get
            {
                Console.WriteLine($"Get IsCompleted={isCompleted}");
                return isCompleted;
            }
        }

        public void OnCompleted(Action continuation)
        {
            Console.WriteLine("B.OnCompleted");
            continuation();//await的第二部分会封装成一个Action在此执行
            isCompleted = true;
        }

        public int GetResult()
        {
            Console.WriteLine("B.GetResult");
            return 4;
        }
    }

    class Program
    {
        static  A CreateAwaitObject()
        {
            Console.WriteLine("A.CreateAwaitObject");
            var t = new A();
            return t;
        } 

        static async void TestAwait(A a)
        {

            Console.WriteLine("Begin Test");
            var result = await a;
            Console.WriteLine($"End Test, Resutl:{result}");
        }

        static void Main(string[] args)
        {
            var a = CreateAwaitObject();
            TestAwait(a);//B.IsCompleted = false;
            Console.WriteLine("\n");
            TestAwait(a);//B.IsCompleted = true;
            Console.ReadKey();
        }
    }

执行结果:

 

如果一个对象可以await,这个对象必须实现GetAwaiter函数,可以是成员方法,也可是扩展方法。GetAwaite需要返回一个对象,返回的对象需要满足3个要求:1、实现INotifyCompletion接口;2、实现GetResult函数;3、实现bool IsCompleted,get属性。

 

从上面例子的执行结果可以很容易看出各个部分的执行顺序,程序执行到await时首先会调用对象的GetAwaiter函数函数,然后调用GetAwaiter返回对象的IsCompleted属性,当IsCompleted=falase时,会调用GetAwaiter返回对象的OnCompleted函数,OnCompleted函数有一个Action参数,这个参数也await将函数分割后第二部分的代码;当IsCompleted=true时,会直接调用await将函数封装后第二部分的代码。

 

3.await与Task

await对象执行顺序与对象的IsCompleted属性有关,在await Task对象时,执行顺序也与Task是否已经完成有关。当Task. IsCompleted=false时,这个时候会调用TaskAwaiter.OnCompleted函数,OnCompleted内部会调用Task.ContinueWith函数,传入OnCompleted的Action参数。这样当Task执行完成后就会继续执行函数中await Task的第二部分的代码。当Task. IsCompleted=true时,程序会直接继续执行await Task的第二部分的代码,和同步执行一样。

如下:

    class Program
    {
        public static async Task<int> AwaitTask1()
        {
            var t = Task.FromResult(5);           
            var r = await t;
            Console.WriteLine("Task {0}", Thread.CurrentThread.ManagedThreadId);
            return r;

        }

        public static async Task<int> AwaitTask2()
        {
            Task<int> t = Task.Run(() =>
            {
                Console.WriteLine("Task {0}", Thread.CurrentThread.ManagedThreadId);
                Task.Delay(2000).Wait();
                return 5;
            });
            var r = await t;
            Console.WriteLine("Task {0}", Thread.CurrentThread.ManagedThreadId);
            return r;

        }

        static void Main(string[] args)
        {
            Console.WriteLine("Main1 {0}", Thread.CurrentThread.ManagedThreadId);
            Thread t = new Thread(() => {
                Console.WriteLine("Thread {0}", Thread.CurrentThread.ManagedThreadId);
                Console.WriteLine("\n");
                AwaitTask1().Wait();
                Console.WriteLine("\n");
                AwaitTask2().Wait();
            });
            t.Start();
            Console.Read();
        }
    }

结果:

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值