(十)异步-什么是异步(1)

一、什么是异步

启动程序时,系统会在内存中创建一个新的进程。

进程: 构成运行程序的资源的集合。这些资源包括虚地址空间、文件句柄和程序运行所需的其他许多东西。

在进程内部,系统创建了一个称为线程的内核对象,它代表了真正执行的程序。(线程是“执行线程”的简称。)一旦进程建立,系统会在 Main 方法的第一行语句处开始线程的执行。

关于线程:

  • 默认情况下,一个进程只包含一个线程(主线程),从程序的开始一直执行到结束。
  • 线程可以派生其他线程(子线程),因此在任意时刻,一个进程都可能包含不同状态的多个线程,它们执行程序的不同部分。
  • 如果一个进程拥有多个线程,它们将共享进程的资源。
  • 系统为处理器执行所调度的单元是线程,不是进程。

不使用异步的示例:
在我的电脑上,书上的示例中 CountCharacters(1, “htttp://microsoft.com” ); 里调用 DownloadString 方法会卡很久,可能传入的这个微软官方网的服务器有问题。(经我经验所知,微软官方网在某些时候就调用加载这一步时常会变得很慢,尤其登录微软账号的时候,显得更明显)于是我就直接换了个百度首页的地址来测试。

 class MyDownloadString
    {
        Stopwatch sw = new Stopwatch();
        const int LargeNumber = 6000000;

        public void DoRun()
        {
            sw.Start();
            int t1 = CountCharacters(1, "https://www.baidu.com/");
            int t2 = CountCharacters(2, "http://www.illustratedcsharp.com");
            CountToALargeNumber(1);
            CountToALargeNumber(2);
            CountToALargeNumber(3);
            CountToALargeNumber(4);
            Console.WriteLine($"Chars in https://www.baidu.com/   :{t1}");
            Console.WriteLine($"Chars in http://www.illustractedcsharp.com   :{t2}");
        }

        //获取网页源代码内容的长度
        private int CountCharacters(int id,string uriString)
        {
            WebClient wc1 = new WebClient();
            //执行此处记录下时间值
            Console.WriteLine("Starting call {0}   :  {1, 4:N0} ms",
                id, sw.Elapsed.TotalMilliseconds);
            //需要时间下载并获取网页源代码
            string result = wc1.DownloadString(new Uri(uriString));
            Console.WriteLine("  Call {0} completed:    {1, 4:N0}ms",
                id, sw.Elapsed.TotalMilliseconds);

            return result.Length;
        }

        //每间隔一次时间,打印结果。
        //该方法在书上是有两个形参的,
        //但是我觉得有些多余(可能作者为了使代码规范化吧)
        //我为了简便就只使用一个形参
        private void CountToALargeNumber(int id)
        {
            //循环作用:为了让程序执行一段时间,才打印一次结果,以方便查看计数值
            //如果不执行循环,以下打印结果很可能都是相同值。
            for (long i = 0; i < LargeNumber; i++)
                ;

            Console.WriteLine("  End counting {0}  :   {1,  4:N0}ms",
                id, sw.Elapsed.TotalMilliseconds);
        }
    }
  
    class Program
    {
        static void Main(string[] args)
        {
            MyDownloadString ds = new MyDownloadString();
            ds.DoRun();

            Console.ReadKey();
        }
    }

输出结果:

Starting call 1 : 1 ms
Call 1 completed: 694ms
Starting call 2 : 694 ms
Call 2 completed: 3,147ms
End counting 1 : 3,188ms
End counting 2 : 3,235ms
End counting 3 : 3,280ms
End counting 4 : 3,323ms
Chars in https://www.baidu.com/ :9269
Chars in http://www.illustractedcsharp.com :5164

由输出结果可知,执行 DoRun 方法时,该方法里的代码都是按照顺序来执行的。也就是说,每调用一行代码之前,都得要等待上一行代码执行完毕之后才能执行。这里的示例中,由于 调用 DownloadString 方法是需要请求网络时间的, 所以需要等待 DownloadString 方法执行完毕之后,才能执行下一行代码。

请添加图片描述

使用异步的示例

根据上面的示例,把CountCharacters 方法重写为异步调用的方法:
把 DownloadString 改为 DownloadStringTaskAsync,即改为异步调用方法。

      class MyDownloadString
    {
        Stopwatch sw = new Stopwatch();
        const int LargeNumber = 6000000;

        public void DoRun()
        {
            sw.Start();
            //此处代码修改
            Task<int> t1 =  CountCharacters(1, "https://www.baidu.com/");
            Task<int> t2 = CountCharacters(2, "http://www.illustratedcsharp.com");
            CountToALargeNumber(1);
            CountToALargeNumber(2);
            CountToALargeNumber(3);
            CountToALargeNumber(4);
            Console.WriteLine($"Chars in https://www.baidu.com/   :{t1.Result}");//此处代码修改
            Console.WriteLine($"Chars in http://www.illustractedcsharp.com   :{t2.Result}");//此处代码修改
        }

        //获取网页源代码内容的长度,此处代码修改
        private async Task<int> CountCharacters(int id, string uriString)
        {
            WebClient wc1 = new WebClient();
            //执行此处记录下时间值
            Console.WriteLine("Starting call {0}   :  {1, 4:N0} ms",
                id, sw.Elapsed.TotalMilliseconds);
            //需要时间下载并获取网页源代码

            var result = await wc1.DownloadStringTaskAsync(new Uri(uriString));//此处代码修改

            wc1.DownloadString(new Uri(uriString));

            Console.WriteLine("  Call {0} completed:    {1, 4:N0}ms",
                id, sw.Elapsed.TotalMilliseconds);

            return result.Length;
        }

        //每间隔一次时间,打印结果
        private void CountToALargeNumber(int id)
        {
            //循环作用:为了让程序执行一段四件,才打印一次结果,以方便查看计数值
            //如果不执行循环,以下打印结果很可能都是相同值。
            for (long i = 0; i < LargeNumber; i++)
                ;

            Console.WriteLine("  End counting {0}  :   {1,  4:N0}ms",
                id, sw.Elapsed.TotalMilliseconds);
        }
    }
  
    class Program
    {
        static void Main(string[] args)
        {
            MyDownloadString ds = new MyDownloadString();
            ds.DoRun();

            Console.ReadKey();
        }
    }

输出结果:

Starting call 1 : 6 ms
Starting call 2 : 237 ms
End counting 1 : 287ms
End counting 2 : 342ms
Call 1 completed: 384ms
End counting 3 : 394ms
End counting 4 : 440ms
Chars in https://www.baidu.com/ :9269
Call 2 completed: 925ms
Chars in http://www.illustractedcsharp.com :5164

由输出结果可知,程序执行到 DownloadStringTaskAsync 方法时,程序则以异步调用的方式进行。这样可以执行 DownloadStringTaskAsync 的同时,又可以执行下一行代码。

但是,最后两行代码,输出的参数从 t1 改为 t1.Result (从 t2 改为 t2.Result),由于Result 为 Task<int> 类型的,所以这两行代码需要等待 DownloadStringTaskAsync执行完毕后,返回其值才能被输出。如果该方法没有执行完毕,则阻塞并等待期值到来。

以上例子使用了异步调用,但是没有开辟新线程。是因为程序主线程异步调用了 DownloadStringTaskAsync 告诉系统它想要获取资源后,然后继续做自己的事情了。如果中间过程中,在后台系统请求网络得出结果后,会告诉主线程并把结果返回给它,它就停止手上的工作继续处理这个事情,处理完后再继续执行之前手上的工作。

请添加图片描述

由以上两个示例的输出结果可知,如果方法里有需要等待的代码块,那么该方法为同步方法时就会比作为异步方法时,需要耗费的时间就比较长。
(不使用异步示例中,该方法执行的整个过程所耗费的时间为3,323ms;而使用异步后,需要时间为925ms。主要差距的原因就在于,同步方法等待资源时,就会停止手上的工作,直到有结果返回时,才能继续工作。而异步方法等待资源的同时,还继续处理其他事情。)

二、async/await 特性的结构

同步方法: 如果某一个程序调用某个方法,并在等待方法执行所有处理后才继续执行。
异步方法: 在完成其所有方法之前就返回到调用方法。
特性:

  • 调用方法: 该方法调用异步方法,然后在异步方法执行其任务的时候继续执行(可能在相同的线程上,也可能在不同的线程上)。
  • 异步方法: 该方法异步执行其工作,然后立即返回到调用方法。
  • awaite 表达式: 用于异步方法内部,指明需要异步执行的任务。一个异步方法可以包含任意多个 await 表达式,不过如果一个都不包含的话编译器会发出警告。(大概警告:如果不使用 awaite 表达式,则会视为同步执行。)

三、async/await 特性的整体结构

//调用方法
static void Main()
{
Task<int> value DoAsyncStuff.CalculateSumAsync(5,6);
}

//异步方法
staic class DoAsyncStuff
{
public static async Task<int> CalculateSumAsync(int i1,int i2)
{
int sum = await TaskEx.Run(() => GetSum(i1,i2));
return sum;
}
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值