15.4 Task 异步匿名函数

 

 

 1             Func<int, Task<int>> func = async x =>
 2                {
 3                    Console.WriteLine("starting x={0}", x);
 4                    await Task.Delay(x);
 5                    Console.WriteLine("ending x={0}", x);
 6                    return x * 2;
 7                };
 8 
 9             Task<int> first1 = func(5);
10             Task<int> first2 = func(3);
11             Console.WriteLine("first1 result = {0}", first1.Result);
12             Console.WriteLine("first2 result = {0}", first2.Result);
13             /*  starting x=5
14             starting x=3
15             ending x=3
16             ending x=5
17             first1 result = 10
18             first2 result = 6   */

此处我故意选择这样的值,以便让第二个操作早于第一个完成。但由于我们要在等待第一个 操作完成后再打印结果(使用 Result 属性,这将阻塞线程直到任务结束。再次强调一遍,运行 这样的代码时要十分谨慎!)

将异步代码放到异步方法中,也可得到同样的结果。 异步匿名函数并不会让我感到特别兴奋,但它也有自己的用途。尽管不能应用于LINQ查询 表达式,但在某些情况下,还是可以实现数据转换的异步执行的。这时,只需以一种略微不同的 方式思考整个过程即可。 在讨论完成分后,我们还会回到这个话题,但首先我想向大家展示一下异步匿名函数特别有 用的一个方面。我之前承诺过,要展示另一种在异步方法开始时及早执行参数验证的方式。你可 能还记得,在进入主操作前,需检查参数值是否为空。代码清单15-10是一个单个方法,其结果 与代码清单15-6中两个分离方法得到的结果完全相同。

 1         static Task<int> ComputeLengthAsync(string text)
 2         {
 3             if (text == null)
 4             {
 5                 throw new ArgumentNullException("text");
 6             }
 7             Func<Task<int>> func = async () => 
 8             {
 9                 await Task.Delay(500);
10                 return text.Length;
11             };
12             return func();
13         }

  你会发现这并不是一个异步方法。如果是的话,异常会被包装到任务里,而不是立即抛出。 但我们还是想返回一个任务,因此在验证 之后,将工作包装到一个异步匿名函数中 ,调用委 托 并返回结果。 尽管这看上去还是有点丑,但比分割成两个方法要清晰多了。不过性能上会蒙受一点损失: 额外的包装会产生额外的代价。这在大多数情况下都没有问题,但如果你编写的库会应用于注重 性能的程序,则应在真实场景中检测成本,然后再决定使用哪种方法。

 

posted @ 2018-12-16 19:59  一只桔子2233  阅读(371)  评论(0编辑  收藏  举报