zoukankan      html  css  js  c++  java
  • await Task传异步Lambda问题

    微软在.NET4.5中升级了C#语言到5.0,加入了await和async语法,极大地方便了广大开发人员的异步编程,也是为了和WinRT API配套,因为这套API充满了异步编程。

    在开发过程中发现有时await不住?!流程还是往下走,觉得可能是使用有问题,于是进行了一下研究,发现了原因。

    看下面的一组代码的运行结果及分析说明,WPF平台,代码在按钮的Click事件中。

    1.

                Debug. WriteLine( "1: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));
                Task. Factory. StartNew( async () =>
                {
                    Debug. WriteLine( "2: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));
                    await Task. Delay(5000);
                    Debug. WriteLine( "3: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));
                });
                Debug. WriteLine( "4: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));

    点一下按钮等运行完再点一下按钮的运行结果为:

    1: 10 03:24:03.478
    4: 10 03:24:03.484
    2: 6 03:24:03.508
    3: 12 03:24:08.509
    1: 10 03:24:12.721
    2: 6 03:24:12.722
    4: 10 03:24:12.722
    3: 12 03:24:17.744

    连点两下按钮的运行结果为:

    1: 10 03:29:50.103
    2: 16 03:29:50.104
    4: 10 03:29:50.104
    1: 10 03:29:50.265
    4: 10 03:29:50.266
    2: 16 03:29:50.266
    3: 17 03:29:55.125
    3: 16 03:29:55.289

    分析:Task.Factory.StartNew,在不加await的情况下,如果传人异步的Lambda,可能先执行Task中的逻辑,也可能先执行后面的逻辑,不能确定,所以这种写法不对。

    2.

                Debug. WriteLine( "1: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));
                await Task. Factory. StartNew( async () =>
                {
                    Debug. WriteLine( "2: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));
                    await Task. Delay(5000);
                    Debug. WriteLine( "3: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));
                });
                Debug. WriteLine( "4: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));

    点一下按钮等运行完再点一下按钮的运行结果为:

    1: 10 03:25:16.212
    2: 14 03:25:16.215
    4: 10 03:25:16.217
    3: 14 03:25:21.217
    1: 10 03:25:44.692
    2: 15 03:25:44.694
    4: 10 03:25:44.695
    3: 16 03:25:49.696

    连点两下按钮的运行结果为:

    1: 10 03:31:13.661
    2: 15 03:31:13.662
    4: 10 03:31:13.663
    1: 10 03:31:13.826
    2: 15 03:31:13.827
    4: 10 03:31:13.828
    3: 15 03:31:18.663
    3: 14 03:31:18.837

    分析:Task.Factory.StartNew,加await的情况下,如果传人异步的Lambda,可以保证先执行Task中的逻辑,再执行后面的逻辑。等等!Task中的逻辑没有执行完,只执行了一部分,如果我们的目的是等Task中的逻辑执行完再执行后面的逻辑,这种写法也是错误的,会产生BUG。

    那正确的写法是什么呢,在VS中把鼠标移到await Task. Factory. StartNew( async () =>的StartNew方法上,可以看到返回值是Task<Task>。Task<Task>是什么意思呢,意思是返回一个返回Task的Task。对Task<Task>进行await会得到Task,要想等待这个Task运行完,还得await。

    3.

                Debug. WriteLine( "1: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));
                await await Task. Factory. StartNew( async () =>
                {
                    Debug. WriteLine( "2: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));
                    await Task. Delay(5000);
                    Debug. WriteLine( "3: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));
                });
                Debug. WriteLine( "4: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));

    点一下按钮等运行完再点一下按钮的运行结果为:

    1: 10 03:33:27.212
    2: 12 03:33:27.213
    3: 7 03:33:32.214
    4: 10 03:33:32.214
    1: 10 03:33:35.142
    2: 12 03:33:35.143
    3: 12 03:33:40.145
    4: 10 03:33:40.146

    连点两下按钮的运行结果为:

    1: 10 03:34:57.375
    2: 3 03:34:57.376
    1: 10 03:34:57.556
    2: 3 03:34:57.557
    3: 3 03:35:02.377
    4: 10 03:35:02.378
    3: 3 03:35:02.557
    4: 10 03:35:02.559

    分析:果然加两个await会得到想要的效果。Task.Factory.StartNew,加await await的情况下,如果传人异步的Lambda,可以保证先执行Task中的全部逻辑,再执行后面的逻辑。

    再来看其他情况。

    4.

                Debug. WriteLine( "1: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));
                Task. Factory. StartNew(() =>
                {
                    Debug. WriteLine( "2: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));
                    Task. Delay(5000). Wait();
                    Debug. WriteLine( "3: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));
                });
                Debug. WriteLine( "4: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));

    点一下按钮等运行完再点一下按钮的运行结果为:

    1: 10 03:53:56.809
    2: 14 03:53:56.809
    4: 10 03:53:56.809
    3: 14 03:54:01.831
    1: 10 03:54:03.441
    2: 3 03:54:03.441
    4: 10 03:54:03.441
    3: 3 03:54:08.463

    连点两下按钮的运行结果为:

    1: 10 03:54:42.533
    4: 10 03:54:42.533
    2: 12 03:54:42.535
    1: 10 03:54:42.707
    4: 10 03:54:42.708
    2: 7 03:54:42.708
    3: 12 03:54:47.536
    3: 7 03:54:47.731

    分析:Task.Factory.StartNew,不加await的情况下,如果传人普通的Lambda,也会出现先执行Task中的部分逻辑,就去执行后面的逻辑的情况。

    在VS中把鼠标移到Task. Factory. StartNew(() =>的StartNew方法上,看到返回的是Task,Task需要await,没加await是不对的。

    5.

                Debug. WriteLine( "1: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));
                await Task. Factory. StartNew(() =>
                {
                    Debug. WriteLine( "2: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));
                    Task. Delay(5000). Wait();
                    Debug. WriteLine( "3: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));
                });
                Debug. WriteLine( "4: " + Thread .CurrentThread .ManagedThreadId + " " + DT(DateTime .Now ));

    点一下按钮等运行完再点一下按钮的运行结果为:

    1: 10 03:55:48.141
    2: 16 03:55:48.143
    3: 16 03:55:53.144
    4: 10 03:55:53.145
    1: 10 03:55:56.560
    2: 16 03:55:56.561
    3: 16 03:56:01.563
    4: 10 03:56:01.564

    连点两下按钮的运行结果为:

    1: 10 04:01:34.54
    2: 16 04:01:34.54
    1: 10 04:01:34.218
    2: 15 04:01:34.219
    3: 16 04:01:39.56
    4: 10 04:01:39.57
    3: 15 04:01:39.227
    4: 10 04:01:39.227

    分析:Task.Factory.StartNew,加await的情况下,如果传人普通的Lambda,能保证先执行Task中的全部逻辑,然后再执行后面的逻辑。

    能不能加两个await,答案是不能,那样不能通过编译。如果强行把Lambda改为异步Lambda,可以编译通过也能得到正确的结果,但Resharper会给出警告,这样做没有必要,也不合适。

    结论:使用Task在背后线程中执行耗时逻辑以避免阻塞UI线程是合适的做法。传入一个Lambda可以方便地实现逻辑,增加代码的可读性。如果Lambda中全是同步逻辑,没有使用await,即为普通Lambda,这时对Task的执行使用await不会有问题。但如果Lambda中需要使用异步API或调用异步方法,就必须改为异步Lambda,这时对Task的执行必须加两个await,才能达到想要的效果,先执行Task中的全部逻辑,然后再执行后面的逻辑。

  • 相关阅读:
    远程桌面下启动MATLAB时的License Manager Error -103错误
    自签名证书和私有CA签名的证书的区别 创建自签名证书 创建私有CA 证书类型 证书扩展名【转】
    XeLaTeX中文模板
    MySQL Connector 卸载
    Anaconda更新源
    命令行远程重启服务器
    gnuplot画图中文标注相关问题
    CentOS 远程桌面相关服务安装笔记
    Windows下python2.7安装64位mysqlclient
    LaTeX 修订
  • 原文地址:https://www.cnblogs.com/clockdotnet/p/4188429.html
Copyright © 2011-2022 走看看