zoukankan      html  css  js  c++  java
  • C#中对异步方法及异步lambda表达式

    这篇文章的目的并不是系统地介绍C#中的awaitasync关键字,而是针对我遇到的一些问题进行记录。

    背景

    await / async

    C#中可以用async标识方法,表示这个方法是异步的。异步方法的返回值必须是voidTask或者Task<T>。例如:

    public static async Task<int> Method(int i)
    {
        await Task.Delay(1000);
        return i;
    }
    • 1
    • 2
    • 3
    • 4
    • 5

    async修饰的lambda表达式

    我们可以用async修饰lambda表达式,标识这个表达式为异步。

    Func<Task<HttpResponseMessage>> lambda = async () =>
    {
        using (var httpClient = new HttpClient())
        {
            var response = await httpClient.GetAsync("https://www.bing.com/");
            return response;
        }
    };
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    可以看到,用async修饰以后,返回值也变为Task<HttpResponseMessage>

    async标记只是针对方法内部,外部只关心返回值类型。

    异步方法返回的时间

    异步方法在遇到await关键字之后就会返回。例如下面的代码:

    private static Stopwatch stopwatch;
    
    static void Main(string[] args)
    {
        stopwatch = Stopwatch.StartNew();
        Task task = MethodAsync();
        Console.WriteLine($"Main: {stopwatch.ElapsedMilliseconds}");
    }
    
    public static async Task MethodAsync()
    {
        Console.WriteLine("Enter method.");
        Task.Delay(500).Wait();
        Console.WriteLine($"Method will return: {stopwatch.ElapsedMilliseconds}");
        await Task.Delay(1000);
        Console.WriteLine($"End of method: {stopwatch.ElapsedMilliseconds}");
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    MethodAsync()方法会在遇到await时返回。于是就有了如下输出:

    Enter method.
    Method will return: 542
    Main: 544
    • 1
    • 2
    • 3

    Task类的一些方法

    Task.Run(Action action)

    我们可以用Task.Run(Action action)来启动一个任务。

    static void Main(string[] args)
    {
        stopwatch = Stopwatch.StartNew();
        var task = Task.Run(MethodAsync);
        task.Wait();
        Console.WriteLine($"Main: {stopwatch.ElapsedMilliseconds}");
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    其中MethodAsync就是上面定义的方法。

    可以推测,MethodAsync执行到第一个await就会返回。Wait()方法也会执行完毕。整段程序执行500多毫秒就应该结束。

    让我们运行一下。

    Enter method.
    Method will return: 544
    End of method: 1546
    Main: 1547
    • 1
    • 2
    • 3
    • 4

    奇怪,MethodAsync返回不久,整个程序就应该结束才是,为什么等待了一秒多,直到MethodAsync全部执行完?

    Task.Run(Func<Task> function)

    原来,Task.Run那里实际调用的是Task.Run(Func<Task> function)这个重载。这个重载返回的Task对象,我们在调用其Wait()成员方法时会等待function返回的Task对象执行结束。我认为应该是为了方便这种情况。

    await Task.Run(async () =>
    {
        using (var httpClient = new HttpClient())
        {
            var response = await httpClient.GetAsync("https://www.bing.com/");
            return response;
        }
    });
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这段代码中,正是由于调用的是Task.Run(Func<Task> function)这个重载,才保证了请求完成后才会继续执行后面的代码。

    System.Threading.Tasks.Parallel类的情况

    System.Threading.Tasks.Parallel类提供了非常方便的并发执行循环的方法。

    Parallel.For(0, 10, i => Console.WriteLine(i));
    • 1

    输出

    0
    2
    6
    4
    5
    8
    9
    7
    3
    1
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    再试试

    Parallel.For(0, 10, async i =>
    {
        await Task.Delay(1000);
        Console.WriteLine(i);
    });
    • 1
    • 2
    • 3
    • 4
    • 5

    输出

    • 1

    是的,输出没有任何内容。

    原因是,ParallelTask不一样,它并不会等待返回的Task,而是在方法返回后就结束。

    所以,如果想确保异步方法完全完成,必须改为同步。

    Parallel.For(0, 10, i =>
    {
        Task.Delay(1000).Wait();
        Console.WriteLine(i);
    });
    • 1
    • 2
    • 3
    • 4
    • 5

    输出

    8
    4
    6
    2
    0
    1
    7
    5
    3
    9
  • 相关阅读:
    document.write("x3cx54")?是加密了吗?
    SQL中以count及sum为条件的查询
    给MyEclipse 10增加SVN功能
    iOS参考工具和资源
    Apple Developer参考资料
    [转]最常见的20个jQuery面试问题及答案
    丢掉鼠标-Mac神软Alfred使用手册
    jQuery执行进度提示窗口的实现(progressbar)
    [转]5个JavaScript面试题
    自己写的POIUtil,主要解决从不同的HSSFWorkbook复制sheet以及根据单元格插入图片等
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/14140207.html
Copyright © 2011-2022 走看看