Hulk' Den

in-depth thinking and keep moving.

对[yield]的浅究到发现[async][await]

  上篇对[foreach]的浅究到发现[yield]写完后,觉得对[yield]还没有理解清楚,想起曾经看过一位大牛的帖子讲的很深刻(链接在此),回顾了下,在这里写出自己的理解,与各位分享。

一、通常的异步

  现在我们假设一种平时经常遇到的情况,现有三个方法,其中funcOne和funcTwo比较耗时需要异步执行,而且他们的逻辑是必须在funcOne执行完后才可以执行funcTwo,同理funcTwo执行完后才能执行funcThree。

  按照这样的设定,通常的做法请看代码段[1]:

public class Program
{
	public delegate void CallBack(string nextName);
	public void funcOne(CallBack callback)
	{
		ThreadPool.QueueUserWorkItem((state1) =>
		{
			Console.WriteLine("[One] async Continue!");
			Console.WriteLine("[One] do something!");
			callback("Called Two");
		});
	}
	public void funcTwo(CallBack callback)
	{
		ThreadPool.QueueUserWorkItem((state2) =>
		{
			Console.WriteLine("[Two] async Continue!");
			Console.WriteLine("[Two] do something!");
			callback("Called Three");
			});
	}
     public void funcThree(CallBack callback)
     {
         Console.WriteLine("[Three] do something!");
         callback("Called ...");
     }
     static void Main()
     {
         Program p = new Program();
         p.funcOne((name1) =>
         {
             Console.WriteLine(name1);
             p.funcTwo((name2) =>
             {
                 Console.WriteLine(name2);
                 //执行funcThree
                 p.funcThree((name3) =>
                 {
                     Console.WriteLine(name3);
                     //当然还有可能继续嵌套
                     Console.WriteLine("End!");
                 });
             });
         });
         Console.Read();
     }
 }

  相信看完代码后我们的感觉是一样的,好繁琐,就是不断的嵌套!那有没有方法可以避免这样呢,也就是说用同步的写法来写异步程序。

二、改进后的异步

  该[yield]粉墨登场了,先看代码段[2]:

//三个方法以及委托CallBack的定义不变,此处不再列出。
//新增了静态的全局变量enumerator,和静态方法funcList.
public static System.Collections.IEnumerator enumerator = funcList();
public static System.Collections.IEnumerator funcList()
{
    Program p = new Program();
    p.funcOne((name1) =>
    {
        enumerator.MoveNext();
    });
    yield return 1;
    Console.WriteLine("Called Two");
    p.funcTwo((name2) =>
    {
        enumerator.MoveNext();
    });
    yield return 2;
    Console.WriteLine("Called Three");
    p.funcThree((name3) =>
    {
        //当然还有可能继续嵌套
    });
    Console.WriteLine("Called ...");
    Console.WriteLine("End!");
    yield return 3;
}

//变化后的Main函数
static void Main()
{
    enumerator.MoveNext();
    Console.Read();
}    

  现在看看,是不是清爽了一些,没有无止尽的嵌套。代码中我们只需要定义一个迭代器,在迭代器中调用需要同步执行的方法,用[yield]来分隔,各方法通过在callback里调用迭代器的MoveNext()方法来保持同步。

  通过这样的实践,我们可以理解为每当[yield return ..],程序会把迭代器的[上下文环境]暂时保存下来,等到MoveNext()时,再调出来继续执行到下一个[yield return ..]。

三、是我想太多

  以上纯属瞎扯,在.net 4.5之后,我们有了神器:async/await,下面看看多么简洁吧。

  代码段[3]:

public class Program
{
    public async Task funcOne()
    {
        Console.WriteLine("[One] async Continue!");
        Console.WriteLine("[One] do something!");
        //await ...
    }
    public async Task funcTwo()
    {
        Console.WriteLine("[Two] async Continue!");
        Console.WriteLine("[Two] do something!");
        //await ...
    }
    public void funcThree()
    {
        Console.WriteLine("[Three] do something!");
    }
    public static async Task funcList()
    {
        Program p = new Program();
        await p.funcOne();
        await p.funcTwo();
        p.funcThree();
        //无尽的嵌套可以继续await下去。
        Console.WriteLine("End!");
    }
    static void Main()
    {
        funcList();
        Console.Read();
    }
}

  我已经感觉到了您的惊叹之情。

  async修饰符将方法指定为异步方法(注意!:异步不异步,并不是async说了算,如果这个方法中没有await语句,就算用了async修饰符,它依然是同步执行,因为它就没有异步基因)。

  被指定为异步方法后,方法的返回值只能为Task、Task<TResult>或者void,返回的Task对象用来表示这个异步方法,你可以对这个Task对象进行控制来操作这个异步方法,详细的可以参考msdn中给出的Task解释与示例代码

  await用于挂起主线程(这里的主线程是相对的),等待这个异步方法执行完成并返回,接着才继续执行主线程的方法。用了await,主线程才能得到异步方法返回的Task对象,以便于在主线程中对它进行操作。如果一个异步方法调用时没有用await,那么它就会去异步执行,主线程不会等待,就像我们代码段[3]中Main方法里对funcList方法的调用那样。

 

  最后就分析到这儿吧,希望各位兄弟博友能一起讨论,共同进步。

  当然,别吝啬您的[赞]哦 :)

 

 更新:现在大家看到的是改进之后的文章,在此谢谢文章下面几位高手的讨论,让我学会了不少。抱拳!

 

  

 

最近的文章

从闭包说起

   世界杯车轮战开始了,连通三天,基本进入世界杯状态。看球也不能忘了玩技术,这次打算把接触c#以来的点滴总结起来,让原本模糊的概念清晰起来,博友们一起来吧!  [闭包]接触这个词的第一感觉就是晦涩难懂,下面我们就来啃一啃。一、邂逅[闭包]  第一次接触闭包是在js里,先来看代码段[1]:function a() { var i = 0; function b() { alert(++i); } return b; } var c = a(...…

C# | .NET继续阅读
更早的文章

对[foreach]的浅究到发现[yield]

  闲来无事,翻了翻以前的代码,做点总结,菜鸟从这里起航,呵呵。一、List的foreach遍历  先上代码段[1]: class Program { static void Main(string[] args) { List<string> dayList = new List<string> { "Sun", "Mon", "Tue", "Wed", "Thr", "Fri", "Sat" }; foreach (var day in day...…

C# | .NET继续阅读