zoukankan      html  css  js  c++  java
  • C#异步编程之:(一)Task对象和lamda表达式探究

    基于TPL的异步编程,最简单的就是使用Task对象,而创建一个Task对象,最简单的就是使用lamda表达式:

            static void Main(string[] args) {
    
                // create the cancellation token source
                CancellationTokenSource tokenSource = new CancellationTokenSource();
                // create the cancellation token
                CancellationToken token = tokenSource.Token;
                int i = 0;
                int value = 10;
    
                // create the first task, which we will let run fully
                Task task1 = new Task(() => {
                    for (i = 0; i < Int32.MaxValue; i++)
                    {
                        value++;
                        bool cancelled = token.WaitHandle.WaitOne(10000);
    
                        if (cancelled) {
                            throw new OperationCanceledException(token);
                        }
                    }
                }, token);
    
                // start task
                task1.Start();
    
                // cancel the token
                tokenSource.Cancel();
    
                // wait for input before exiting
                Console.WriteLine("Main method complete. Press enter to finish with value = " + value.ToString());
                Console.ReadLine();
            }

    好,在Task对象的lamda表达式里面,我们引用了外部的成员:i, value, token。那么到底.Net编译器做了什么,能让我们访问这些外部成员呢?原理很简单,这个是经过ILSpy反编译之后的源代码(把特殊的反编译选项全部disable掉):

    // Listing_13.Listing_13
    private static void Main(string[] args)
    {
    	Listing_13.<>c__DisplayClass1 <>c__DisplayClass = new Listing_13.<>c__DisplayClass1();
    	CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
    	<>c__DisplayClass.token = cancellationTokenSource.Token;
    	<>c__DisplayClass.i = 0;
    	<>c__DisplayClass.value = 10;
    	Task task = new Task(new Action(<>c__DisplayClass.<Main>b__0), <>c__DisplayClass.token);
    	task.Start();
    	cancellationTokenSource.Cancel();
    	Console.WriteLine("Main method complete. Press enter to finish with value = " + <>c__DisplayClass.value.ToString());
    	Console.ReadLine();
    }
    

    .Net编译器,为lamda表达式创建了一个新的类,<>c__DislayClass(这个很奇怪,我记得await编译出来的,是生成一个Structure,难道是.Net 4.5做了优化了?),而lamda表达式本身变成了一个函数<Main>b_0, 引用的外部成员,都变车了类成员.i, .value, .token,注意这里没有多余的变量产生。注意,在整个main函数里面,这里所有被lamda引用的变量

    好,我们来看看这个<>c__DisplayClass是什么东西:


    [CompilerGenerated]
    private sealed class <>c__DisplayClass1
    {
    	public CancellationToken token;
    	public int i;
    	public int value;
    	public void <Main>b__0()
    	{
    		this.i = 0;
    		while (this.i < 2147483647)
    		{
    			this.value++;
    			bool flag = this.token.WaitHandle.WaitOne(10000);
    			if (flag)
    			{
    				throw new OperationCanceledException(this.token);
    			}
    			this.i++;
    		}
    	}
    }
    

    特性CompilerGenerated表明,这个是编译器产生的,所以类名可以使用<>前缀。这个类没什么特别,和lamda表达式一摸一样!所以,现在一切都清晰明了了!哈哈,就像侯捷所说,源码之前,了无秘密。


    要注意的是,编译器把外部变量都变成了DisplayClass的成员,所以假如异步方法还没有执行前(或者没有执行完前),就改变了外部变量的值,会影响异步方法的。(相当于修改了成员变量的值)

  • 相关阅读:
    Django中获取参数(路径,查询,请求头,请求体)
    正则表达式基础、原理
    每日总结【2020/02/12】
    【家庭记账本】Android开发(初稿)
    每日总结【2020/02/11】
    【家庭记账本】Android开发日记(九)
    【家庭记账本】Android开发日记(八)
    【家庭记账本】Android开发(提交稿件)
    每日总结【2020/02/09】
    《构建之法》阅读笔记(三)
  • 原文地址:https://www.cnblogs.com/puncha/p/3877016.html
Copyright © 2011-2022 走看看