zoukankan      html  css  js  c++  java
  • C#中的异步陷阱

    本文主要介绍异步编程中,常见的异步陷阱:

    1、Async没有异步运行

    我们来看下面代码,猜测他是如何打印出下面的三个字符串:

    /*Gotcha #1: Async does not run asynchronously*/
    static void Main(string[] args)
    {
        Console.WriteLine("begin" + "" + DateTime.Now.ToString() + "");
        var child = Gotcha1.WorkThenWait();
        Console.WriteLine("started" + "" + DateTime.Now.ToString() + "");
        child.Wait();
        Console.WriteLine("completed" + "" + DateTime.Now.ToString() + "");
        Console.ReadKey();
    }
    public static async Task WorkThenWait()
    {
        Thread.Sleep(5000);
        Console.WriteLine("work" + "" + DateTime.Now.ToString() + "");
        await Task.Delay(10000);
    }

    看这段代码,如果你猜想,他会按顺序打印出“begin“,”started”,“work”,“completed”,那样的话,你就错了。这段代码会输出“begin“,“work”,“started”,“completed”。

    可以看出来,本段代码本来的猜想是,由于WorkThenWait()方法中包含繁重耗时的任务(这里为Thread.Sleep(5000)),所以打算让他异步执行这个方法,但是,可以看出问题出在await关键字用在了该段繁重耗时任务之后,所以后面的child.Wait()方法只是等待了Task.Delay(10000)的执行。

    执行结果如下:

     2、忽略结果

    我们来看下面代码,猜测他是如何执行的,是否会等待:

    /*Gotcha #2: Ignoring results*/
    static void Main(string[] args)
    {
        Gotcha2.Handler();
        Console.ReadKey();
    }
    public static async Task Handler()
    {
        Console.WriteLine("Before");
        Task.Delay(5000);
        Console.WriteLine("After");
    }

    看这段代码,你是否期待它会打印“Before”,等待5秒之后,再打印“After”?当然,又错了。他会立即打印两条字符串,直接没有任何等待。问题出在,虽然Task.Delay返回了Task返回值,但是我们忘记了使用await关键字去等待直到他完成。

    执行结果如下:

     3、Async void方法

    我们来看下面代码,判断下是否能成功打印出异常信息:

    /*Gotcha #3: Async void methods*/
    static void Main(string[] args)
    {
        Gotcha3.CallThrowExceptionAsync();
        Console.ReadKey();
    }
    
    private static async void ThrowExceptionAsync()
    {
        throw new InvalidOperationException();
    }
    
    public static void CallThrowExceptionAsync()
    {
        try
        {
            ThrowExceptionAsync();
        }
        catch (Exception)
        {
            Console.WriteLine("Failed");
        }
    }

    你是否觉得这段代码会打印“Failed”?当然,这个异常没有被捕获到,因为ThrowExceptionAsync方法开始执行并立即返回(这个异常发生在background thread内部)。问题根源在于:对于例如 async void Foo(){...},这种方法,C#编译器将生成一个返回void的方法,然后它会在后台创建一个task并执行。这意味着你无法知道这项工作实际何时发生。

     4、Async void lambda函数

    如果你将异步lambda函数作为委托传递给某个方法时。在这种情况下,C#编译器将从委托类型推断方法的类型。如果使用Action delegate,则编译器生成async void function(这种后台启动线程工作并返回void)。如果使用Func<Task> delegate,编译器讲生成返回Task的function。

    我们来看下面代码,判断这段代码是在5秒之后执行完(等待所有的Task完成sleeping),或者它立即完成了?

    /*Gotcha #4: Async void lambda functions*/
    static void Main(string[] args)
    {
        Gotcha4.Test();
        Console.WriteLine("ok");
    }
    
    public  static void Test()
    {
        Parallel.For(0, 10, async i => {
            await Task.Delay(5000);
        });
    }

    当然,直接看For无法判断出他是否等待,我们借助小工具看下如下代码的源码:

    public void TestMethod()
    {
        Parallel.For(0, 10, async i => {
            await Task.Delay(5000);
        });
    }
    // AsyncGotchasLib.Class1
    public void TestMethod()
    {
        int arg_23_0 = 0;
        int arg_23_1 = 10;
        Action<int> arg_23_2;
        if ((arg_23_2 = Class1.<>c.<>9__0_0) == null)
        {
            arg_23_2 = (Class1.<>c.<>9__0_0 = new Action<int>(Class1.<>c.<>9.<TestMethod>b__0_0));
        }
        Parallel.For(arg_23_0, arg_23_1, arg_23_2);
    }

    可以看到For中,lambda函数最终编译为了Action Delegate,因此这段代码不会等待5秒,会立即执行完成,它的等待会在后台线程执行。

     5、嵌套任务

    看下面代码,问题是,下面两个print直接会不会等待5秒:

    /*Gotcha #5: Nesting of tasks*/
    static void Main(string[] args)
    {
        Gotcha5.Test();
        Console.ReadKey();
    }
    
    public async static void Test()
    {
        Console.WriteLine("Before");
        await Task.Factory.StartNew(
          async () => { await Task.Delay(5000); Console.WriteLine("后台线程等待5秒后"); });
        Console.WriteLine("After");
    }

    同样,相当意外,并没有在两个打印(“Before”,“After”)之间等待。为什么?StratNew 方法接受一个委托,并返回一个Task<T>,其中T表示又delegate返回的类型。在这个方法中,这个delegate返回Task,因此,我们获得的结果为Task<Task>。使用await关键字仅仅等待外部Task的完成(它将立即返回内部task),这代表内部task将被忽略,内部task将在后台线程执行。

  • 相关阅读:
    1094. Car Pooling
    121. Best Time to Buy and Sell Stock
    58. Length of Last Word
    510. Inorder Successor in BST II
    198. House Robber
    57. Insert Interval
    15. 3Sum java solutions
    79. Word Search java solutions
    80. Remove Duplicates from Sorted Array II java solutions
    34. Search for a Range java solutions
  • 原文地址:https://www.cnblogs.com/SimplePerson/p/7395513.html
Copyright © 2011-2022 走看看