天天看点

C# 简述异步编程

  要说清楚什么是 异步 ,那应当结合 同步 来进行比较说明。在计算机编程中,同步 是指一个程序调用另一个程序时,需要等待被调用程序执行完毕之后调用程序才能继续往下执行;异步 是指一个程序调用另一个程序,调用程序在被调用程序未执行完(执行过程中),就在调用位置继续往下执行的操作。或许,这么说你可能还是不太理解什么是同步,什么是异步,不要紧,且听我下面讲个故事。

一个故事

  在很久以前,有一个程序员他是 18:00 下班,他回家有很多很多事情要做,各种事情忙活儿完了过后他发现已经快晚上十点钟了,这怎么行了,连休息一下的时间都没有。于是,他准备写一个程序来解决这个问题,其中要做的事情就包括以下这些内容:

序号 要做的事情 所需要的事件
1 做饭吃饭 1.5 个小时
2 洗澡 30 分钟
3 洗衣服 1个小时
4 刷一会儿抖音 30 分钟
5 写这篇博客 1 个小时

  于是,这个程序的代码如下所示:

using System;
using System.Diagnostics;
using System.Threading;

namespace AsynDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("这个程序员下班回到家里...");
            Stopwatch stopwatch = Stopwatch.StartNew();
            Zuofan();
            Xizao();
            Xiyifu();
            Douyin();
            XieBlog();
            double dt = (double)stopwatch.ElapsedMilliseconds / (6 * 1000);
            Console.WriteLine($"他看了看时间,发现现在已经 {Convert.ToDateTime("18:00").AddHours(dt).ToShortTimeString()} ");
            Console.WriteLine($"他一共花了 {dt:F1} 个小时");
            Console.ReadLine();
        }
        private static void Zuofan()
        {
            Console.WriteLine("开始做饭/吃饭...");
            Thread.Sleep(9 * 1000);
            Console.WriteLine("做饭/吃饭结束。");
        }
        private static void Xizao()
        {
            Console.WriteLine("开始洗澡...");
            Thread.Sleep(3 * 1000);
            Console.WriteLine("洗澡结束。");
        }
        private static void Xiyifu()
        {
            Console.WriteLine("开始洗衣服...");
            Thread.Sleep(6 * 1000);
            Console.WriteLine("洗衣服结束。");
        }
        private static void Douyin()
        {
            Console.WriteLine("开始刷抖音...");
            Thread.Sleep(3 * 1000);
            Console.WriteLine("刷抖音结束。");
        }
        private static void XieBlog()
        {
            Console.WriteLine("开始写博客...");
            Thread.Sleep(6 * 1000);
            Console.WriteLine("写博客结束。");
        }
    }
}
           

  执行程序后发现,他一共花了 4.5 个小时,而且等什么事情都忙完后已经十点半了。以下是程序输出结果:

这个程序员下班回到家里...
开始做饭/吃饭...
做饭/吃饭结束。
开始洗澡...
洗澡结束。
开始洗衣服...
洗衣服结束。
开始刷抖音...
刷抖音结束。
开始写博客...
写博客结束。
他看了看时间,发现现在已经 22:30
他一共花了 4.5 个小时
           

  原来,他在做饭的时候一直专注于做饭,洗衣服的时候只是忙着洗衣服。后来,他看了看 微软的官方文档 ,才明白,做饭之前可以先把衣服放到洗衣机里面开始洗着,吃饭的时候还可以看看抖音。这样子并行的做法让他的时间提高了利用率,下面是他改进后的程序:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

namespace AsynDemo
{
    class Program
    {
        static async Task Main(string[] args)
        {
            Console.WriteLine("这个程序员下班回到家里...");
            Stopwatch stopwatch = Stopwatch.StartNew();
            List<Task> tasks = new List<Task>()
            {
                ZuofanAsync(),XizaoAsync(),XiyifuAsync(),DouyinAsync(),XieBlogAsync()
            };
            await Task.WhenAll(tasks);
            double dt = (double)stopwatch.ElapsedMilliseconds / (6 * 1000);
            Console.WriteLine($"他看了看时间,发现现在已经 {Convert.ToDateTime("18:00").AddHours(dt).ToShortTimeString()} ");
            Console.WriteLine($"他一共花了 {dt:F1} 个小时");
            Console.ReadLine();
        }
        private static async Task ZuofanAsync()
        {
            Console.WriteLine("开始做饭/吃饭...");
            await Task.Delay(9 * 1000);
            Console.WriteLine("做饭/吃饭结束。");
        }
        private static async Task XizaoAsync()
        {
            Console.WriteLine("开始洗澡...");
            await Task.Delay(3 * 1000);
            Console.WriteLine("洗澡结束。");
        }
        private static async Task XiyifuAsync()
        {
            Console.WriteLine("开始洗衣服...");
            await Task.Delay(6 * 1000);
            Console.WriteLine("洗衣服结束。");
        }
        private static async Task DouyinAsync()
        {
            Console.WriteLine("开始刷抖音...");
            await Task.Delay(3 * 1000);
            Console.WriteLine("刷抖音结束。");
        }
        private static async Task XieBlogAsync()
        {
            Console.WriteLine("开始写博客...");
            await Task.Delay(6 * 1000);
            Console.WriteLine("写博客结束。");
        }
    }
}
           

  通过以上异步的方式改进了程序后,我们发现时间缩短成了两个小时之内就能完成所有的事情。

走进异步编程

  由以上的故事我们可以看到,C# 中的

Async

Await

关键字是异步编程的核心。

  由关键字

Async

申明定义异步方法,在异步方法中遇到

Await

关键字的指令将返回到异步方法调用的位置调用方法将继续往下执行。如果在异步方法中不存在

Await

关键字,那么定义的异步方法将和普通的同步方法呈现出一样的效果。

无返回值异步函数

  如果说我这里具有大量的程序计算,但是却又不需要具有返回值,那么可以创建一个无返回值的异步方法,如下所示:

using System;
using System.Threading.Tasks;

namespace AsynDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("程序将要进入异步方法...");
            FuncAsync(); // 异步函数调用,不需要等待异步函数执行完毕,程序在这里将在 Main 方法中继续向下执行
            Console.WriteLine("异步方法还没有结束,程序就在调用位置继续向下执行...");
            Console.ReadLine();
        }
        private static async void FuncAsync()
        {
            Console.WriteLine("开始执行异步方法...");
            await Task.Run(()=> { // 匿名函数,打印出 九九乘法表
                for (int i = 1; i < 10; i++)
                {
                    for (int j = 1; j <= i; j++)
                    {
                        Console.Write($"{j}×{i}={j * i}\t");
                    }
                    Console.WriteLine();
                }
            });
            Console.WriteLine("异步方法结束。");
        }
    }
}
           

有返回值异步函数

  以下案例演示了有返回值异步函数过程。在有返回值异步函数中应当包含

return

语句。

using System;
using System.Threading.Tasks;

namespace AsynDemo
{
    class Program
    {
        static async Task Main(string[] args)
        {
            Console.Write("请输入一个正整数:");
            int n = Convert.ToInt32(Console.ReadLine());
            double dous = await FuncAsync(n);
            Console.WriteLine($"计算结果:{dous}");
            Console.WriteLine("异步方法执行结束,程序等待异步方法返回后继续向下执行...");
            Console.ReadLine();
        }
        private static async Task<double> FuncAsync(int n)
        {
            Console.WriteLine("开始执行异步方法...");
            double sum = await Task.Run(() =>
            { // 匿名函数,返回 阶乘
                int jc = 1;
                for (int i = 1; i <= n; i++)
                {
                    jc *= i;
                }
                return jc;
            });
            Console.WriteLine("异步方法结束。");
            return sum;
        }
    }
}
           

  由以上两个案例可看出,无返回值异步方法可以用

Void

或者

Task

来标记,有返回值可以采用

Task<T>

来标记。

异步方法 的应用,能够提高我们的应用程序处理任务的速度,提高系统性能,特别是在

UI

交互的方面,当进行以此

UI

操作之后,异步方法继续执行业务处理直到结束,而

UI

上并不会产生停顿等待的过程,这也提高了用户体验。

这里贴上官网的学习路径:异步编程

继续阅读