基于任务的异步模式

[你必须知道的异步编程]——基于任务的异步模式

本专题概要

  • 引言
  • 什么是TAP——基于任务的异步模式介绍
  • 如何使用TAP——使用基于任务的异步模式来异步编程
  • TAP与APM或EAP可以转换吗?——与其他异步模式的转换
  • 小结

 

一、引言

  在上两个专题中我为大家介绍.NET 1.0中的APM和.NET 2.0中的EAP,在使用前面两种模式进行异步编程的时候,大家多多少少肯定会感觉到实现起来比较麻烦, 首先我个人觉得,当使用APM的时候,首先我们要先定义用来包装回调方法的委托,这样难免有点繁琐, 然而使用EAP的时候,我们又需要实现Completed事件和Progress事件,上面两种实现方式感觉都有点繁琐,同时微软也意思到了这点,所以在.NET 4.0中提出了一个新的异步模式——基于任务的异步模式,该模式主要使用System.Threading.Tasks.Task和Task<T>类来完成异步编程,相对于前面两种异步模式来讲,TAP使异步编程模式更加简单(因为这里我们只需要关注Task这个类的使用),同时TAP也是微软推荐使用的异步编程模式,下面就具体为大家分享下本专题的内容.

二、什么是TAP——基于任务的异步模式介绍

基于任务的异步模式(Task-based Asynchronous Pattern,TAP)之所以被微软所推荐,主要就它使用简单,基于任务的异步模式使用单个方法来表示异步操作的开始和完成,然而异步编程模型(APM)却要求BeginXxx和EndXxx两个方法来分别表示异步操作的开始和完成(这样使用起来就复杂了),然而,基于事件的异步模式(EAP)要求具有Async后缀的方法和一个或多个事件、事件处理程序和事件参数。看到这里,是不是大家都有这样一个疑问的——我们怎样区分.NET类库中的类实现了基于任务的异步模式呢? 这个识别方法很简单,当看到类中存在TaskAsync为后缀的方法时就代表该类实现了TAP, 并且基于任务的异步模式同样也支持异步操作的取消进度的报告的功能,但是这两个实现都不像EAP中实现的那么复杂,因为如果我们要自己实现EAP的类,我们需要定义多个事件和事件处理程序的委托类型和事件的参数(具体可以查看上一专题中的BackgroundWorker剖析部分),但是在TAP实现中,我们只需要通过向异步方法传入CancellationToken 参数,因为在异步方法内部会对这个参数的IsCancellationRequested属性进行监控,当异步方法收到一个取消请求时,异步方法将会退出执行(具体这点可以使用反射工具查看WebClient的DownloadDataTaskAsync方法,同时也可以参考我后面部分自己实现基于任务的异步模式的异步方法。),在TAP中,我们可以通过IProgress<T>接口来实现进度报告的功能,具体实现可以参考我后面的程序部分。

目前我还没有找到在.NET 类库中实现了基于任务的异步模式的哪个类提供进度报告的功能,下面的将为大家演示这个实现,并且也是这个程序的亮点,同时通过自己实现TAP的异步方法来进一步理解基于任务的异步模式。

三、如何使用TAP——使用基于任务的异步模式来异步编程

 看完上面的介绍,我们是不是很迫不及待想知道如何自己实现一个基于任务的异步模式的异步方法的,并且希望只需要这个方法就可以完成异步操作的取消和进度报告的功能的(因为EAP中需要实现其他的事件和定义事件参数类型,这样的实现未免过于复杂),下面就基于上专题中实现的程序用基于任务的异步模式来完成下。下面就让我们实现自己的异步方法(亮点为只需要一个方法就可以完成进度报告和异步操作取消的功能):

复制代码
//  Download File
        // CancellationToken 参数赋值获得一个取消请求
        // progress参数负责进度报告
        private void DownLoadFile(string url, CancellationToken ct, IProgress<int> progress)
        {
            HttpWebRequest request = null;
            HttpWebResponse response = null;
            Stream responseStream = null;
            int bufferSize = 2048;
            byte[] bufferBytes = new byte[bufferSize];
            try
            {
                request = (HttpWebRequest)WebRequest.Create(url);
                if (DownloadSize != 0)
                {
                    request.AddRange(DownloadSize);
                }

                response = (HttpWebResponse)request.GetResponse();
                responseStream = response.GetResponseStream();
                int readSize = 0;
                while (true)
                {
                    // 收到取消请求则退出异步操作
                    if (ct.IsCancellationRequested == true)
                    {
                        MessageBox.Show(String.Format("下载暂停,下载的文件地址为:{0}\n 已经下载的字节数为: {1}字节", downloadPath, DownloadSize));

                        response.Close();
                        filestream.Close();
                        sc.Post((state) =>
                        {
                            this.btnStart.Enabled = true;
                            this.btnPause.Enabled = false;
                        }, null);

                        // 退出异步操作
                        break;
                    }

                    readSize = responseStream.Read(bufferBytes, 0, bufferBytes.Length);
                    if (readSize > 0)
                    {
                        DownloadSize += readSize;
                        int percentComplete = (int)((float)DownloadSize / (float)totalSize * 100);
                        filestream.Write(bufferBytes, 0, readSize);

                        // 报告进度
                        progress.Report(percentComplete);
                    }
                    else
                    {
                        MessageBox.Show(String.Format("下载已完成,下载的文件地址为:{0},文件的总字节数为: {1}字节", downloadPath, totalSize));

                        sc.Post((state) =>
                        {
                            this.btnStart.Enabled = false;
                            this.btnPause.Enabled = false;
                        }, null);

                        response.Close();
                        filestream.Close();
                        break;
                    }
                }      
            }
            catch (AggregateException ex)
            {
                // 因为调用Cancel方法会抛出OperationCanceledException异常
                // 将任何OperationCanceledException对象都视为以处理
                ex.Handle(e => e is OperationCanceledException);
            }
        }
复制代码

这样只需要上面的一个方法,我们就可以完成上一专题中文件下载的程序,我们只需要在下载按钮的事件处理程序调用该方法和在暂停按钮的事件处理程序调用CancellationTokenSource.Cancel方法即可,具体代码为:

复制代码
        // Start DownLoad File
        private void btnStart_Click(object sender, EventArgs e)
        {
            filestream = new FileStream(downloadPath, FileMode.OpenOrCreate);
            this.btnStart.Enabled = false;
            this.btnPause.Enabled = true;

            filestream.Seek(DownloadSize, SeekOrigin.Begin);

            // 捕捉调用线程的同步上下文派生对象
            sc = SynchronizationContext.Current;
            cts = new CancellationTokenSource();
            // 使用指定的操作初始化新的 Task。

            task = new Task(() => Actionmethod(cts.Token), cts.Token);

            // 启动 Task,并将它安排到当前的 TaskScheduler 中执行。 
            task.Start();

            //await DownLoadFileAsync(txbUrl.Text.Trim(), cts.Token,new Progress<int>(p => progressBar1.Value = p));
        }

        // 任务中执行的方法
        private void Actionmethod(CancellationToken ct)
        {
            // 使用同步上文文的Post方法把更新UI的方法让主线程执行
            DownLoadFile(txbUrl.Text.Trim(), ct, new Progress<int>(p => 
                {
                    sc.Post(new SendOrPostCallback((result)=>progressBar1.Value=(int)result),p);
                }));
        }

        // Pause Download
        private void btnPause_Click(object sender, EventArgs e)
        {
            // 发出一个取消请求
            cts.Cancel();
        }
复制代码

下面看看基于任务的异步模式的实现效果如何的,运行结果:

基于任务的异步模式_第1张图片

点击确定按钮之后,Download按钮会重新变成可用,此时我们可以继续点击Download按钮来下载进行下载,下载完成之后会下载完成弹出框,运行结果如下:

基于任务的异步模式_第2张图片

四、TAP与APM或EAP可以转换吗?——与其他异步模式的转换

从上面的程序代码我们可以清楚的发现——基于任务的异步模式确实比前面的两种异步模式更加简单使用,所以,从.NET Framework 4.0开始,微软推荐使用TAP来实现异步编程,这里就涉及之前用APM或EAP实现的程序如何迁移到用TAP实现的问题的,同时.NET Framwwork对他们之间的转换了也做了很好的支持。

4.1 将APM转换为TAP 

System.Threading.Tasks命名空间中,有一个TaskFactory(任务工程)类,我们正可以利用该类的FromAsync方法来实现将APM转换为TAP,下面就用基于任务的异步模式来实现在异步编程模型博文中例子。

复制代码
        // 大家可以对比这两种实现方式
        #region 使用APM实现异步请求
        private void APMWay()
        {
            WebRequest webRq = WebRequest.Create("http://msdn.microsoft.com/zh-CN/");
            webRq.BeginGetResponse(result =>
            {
                WebResponse webResponse = null;
                try
                {
                    webResponse = webRq.EndGetResponse(result);
                    Console.WriteLine("请求的内容大小为: " + webResponse.ContentLength);
                }
                catch (WebException ex)
                {
                    Console.WriteLine("异常发生,异常信息为: " + ex.GetBaseException().Message);
                }
                finally
                {
                    if (webResponse != null)
                    {
                        webResponse.Close();
                    }
                }
            }, null);
        }

        #endregion

        #region 使用FromAsync方法将APM转换为TAP
        private void APMswitchToTAP()
        {
            WebRequest webRq = WebRequest.Create("http://msdn.microsoft.com/zh-CN/");
            Task.Factory.FromAsync<WebResponse>(webRq.BeginGetResponse, webRq.EndGetResponse, null, TaskCreationOptions.None).
                ContinueWith(t =>
                {
                    WebResponse webResponse = null;
                    try
                    {
                        webResponse = t.Result;
                        Console.WriteLine("请求的内容大小为: " + webResponse.ContentLength);
                    }
                    catch (AggregateException ex)
                    {
                        if (ex.GetBaseException() is WebException)
                        {
                            Console.WriteLine("异常发生,异常信息为: " + ex.GetBaseException().Message);
                        }
                        else
                        {
                            throw;
                        }
                    }
                    finally
                    {
                        if (webResponse != null)
                        {
                            webResponse.Close();
                        }
                    }
                });
        }

        #endregion
复制代码

上面代码演示了使用APM的原始实现方式以及如何使用FromAsync方法把APM的实现方式转换为TAP的实现方法,把这两种方式放在一起,一是可以帮助大家做一个对比,使大家更容易明白APM与TAP的转换,二是大家也可以通过上面的对比明白TAP与APM的区别。

4.2 将EAP转化为TAP

处理APM可以升级为用TAP来实现外,对于EAP,我们同样可以对其转换为TAP的方式,下面代码演示了如何将EAP转换为TAP的实现方式:

复制代码
#region 将EAP转换为TAP的实现方式

            // webClient类支持基于事件的异步模式(EAP)
            WebClient webClient = new WebClient();

            // 创建TaskCompletionSource和它底层的Task对象
            TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();

            // 一个string下载好之后,WebClient对象会应发DownloadStringCompleted事件
            webClient.DownloadStringCompleted += (sender, e) =>
            {
                // 下面的代码是在GUI线程上执行的
                // 设置Task状态
                if (e.Error != null)
                {
                    // 试图将基础Tasks.Task<TResult>转换为Tasks.TaskStatus.Faulted状态
                    tcs.TrySetException(e.Error);
                }
                else if (e.Cancelled)
                {
                    // 试图将基础Tasks.Task<TResult>转换为Tasks.TaskStatus.Canceled状态
                    tcs.TrySetCanceled();
                }
                else
                {
                    // 试图将基础Tasks.Task<TResult>转换为TaskStatus.RanToCompletion状态。
                    tcs.TrySetResult(e.Result);
                }
            };

            // 当Task完成时继续下面的Task,显示Task的状态
            // 为了让下面的任务在GUI线程上执行,必须标记为TaskContinuationOptions.ExecuteSynchronously
            // 如果没有这个标记,任务代码会在一个线程池线程上运行
            tcs.Task.ContinueWith(t =>
            {
                if (t.IsCanceled)
                {
                    Console.WriteLine("操作已被取消");
                }
                else if (t.IsFaulted)
                {
                    Console.WriteLine("异常发生,异常信息为:" + t.Exception.GetBaseException().Message);
                }
                else
                {
                    Console.WriteLine(String.Format("操作已完成,结果为:{0}", t.Result));
                }
            }, TaskContinuationOptions.ExecuteSynchronously);

            // 开始异步操作
            webClient.DownloadStringAsync(new Uri("http://msdn.microsoft.com/zh-CN/"));
            #endregion
复制代码

五、小结

  本专题关于TAP的内容就介绍到这里了,本专题主要以实现一个文件下载程序要讲述基于任务的异步模式所带来的简便,这个也是.NET 4.0中提出TAP的原因所在吧,最后介绍了TAP与APM和EAP模式之间的转化,通过这部分大家可以清楚知道以前的异步实现如何向新的异步模式的迁移,以及从他们的转换实现代码中也可以比较他们之间的不同。然而在.NET 4.5中,微软对异步编程又做了更好的支持——提供了async和await两个关键字,这两个关键字使我们异步编程如同步编程一样的简单,彻底改变了实现异步编程所面临的委托回调,跨线程访问控件等问题,具体这部分内容,我将在下个专题中为大家介绍。

本专题源码下载:http://files.cnblogs.com/zhili/TaskbasedAsynchronousPattern(TAP).zip


    如果,您认为阅读这篇博客对你有帮助,点击一下左下角的[推荐]
    如果,你想了解我的动态,点击一下左下角的[关注我]
    因为,你们的支持正是我学习的动力 
    如需转载,请注明出处

    如果对文章有任何问题,都可以再评论中留言,我会尽可能的答复您,谢谢你的阅读

 

 

 1. 本文观点带有明显的主观倾向性错误,主观的原因是从各个方面了解,作者本身没有大型的.net项目的开发经验,

  仅仅自己的主观就认为:"很多采用Windows平台.net架构的大型网站都面临了架构上的扩展问题",到底是人的问题,还是技术本身问题,

  作者本身没有深入调查、采访,只根据自己的主观感觉和片面资料说事

 

 2. 本文作者为宣称“全球最大中文IT社区,为IT专业技术人员提供最全面的信息传播和服务平台”的CTO所写,

 严重违反了技术“中立性观点”的价值主张,显然和其之前的“JavaEye.com”所站的偏向立场使然。

 和“自己的熟悉的技术就是好”的假设成立,不熟悉的技术就是不好。

 

 3. 看图:

基于任务的异步模式_第3张图片

 

4.我试着批注一下原文:

 

 

在互联网行业,基于Unix/Linux的网站系统架构毫无疑问是当今主流的架构解决方案,这不仅仅是因为Linux本身足够的开放性,更因为围绕传统Unix/Linux社区有大量的成熟开源解决方案,覆盖了网站应用扩展的方方面面。

我记得十几年前第一波互联网浪潮的时代,采用Windows平台ASP架构的大型网站是非常普及的,而如今采用Windows平台.net架构的大流量知名网站已经凤毛麟角了。很多采用Windows平台.net架构的大型网站都面临了架构上的扩展问题:

例如国外的SNS网站MySpace网站面临过很严重的性能扩展问题,国内电商网站京东也不止一次受困于架构扩展带来了性能瓶颈。因此,一些Windows平台.net架构为主的网站,不得不考虑“去.net化”,抛弃.net,全面迁移到以Java为主的架构上。

但是大型网站的架构迁移,本身也是伤筋动骨的事情,风险极高,成功案例尚不多见,失败案例俯拾皆是,这是因为:

  1. 架构迁移是在现有业务系统上进行的,并不是从容的开发新产品,有足够的时间测试和完善,相当于给正在高空飞行的客机换引擎,必须一次切换成功,没有第二次机会,稍有差池,就会机毁人亡。而我们都知道,新开发一个大型系统,没有上线磨合和完善过,无法做到一次100%完美。

  2. 架构迁移意味着老的研发团队将被淘汰,而往往老团队体系随着公司壮大已经掌握了足够话语权,新架构的团队在公司又根基未稳,出于维护自身利益的本能,新旧团队之间很容易爆发政治斗争,相互排挤,导致迁移失败。

点评:上述观点,是我没有亲身经历,靠我所想像得出的结论,所以仅供参考

5173“去.net化”的教训  建议改为:我所听说的 5173 从“.net” 到使用“Java”的一些情况

5173网站以游戏装备交易起家,曾经在客户端网络游戏发展黄金时期,迎来了业务爆发性的增长期。此时,用.net架构开发的网站已经不堪重负,由于现有的.net研发团队长期无法解决网站的性能问题,5173决定将网站从.net架构全面迁移到Java为主的架构上。为此,5173花了很大的代价,从淘宝和SUN公司挖了很多工程师,组成了一个六七十人的Java架构研发部门。

新的Java研发部门开发新的5173网站,而老的.net研发部门维护现有的5173网站,两个部门并行工作,两个版本的网站并行运行,这带来了公司内部激烈的政治斗争,新开发完成的Java版本的5173网站从未正式上线过,老的.net研发团队在面临严重生存威胁的情况下,努力解决了一些核心的可用性和稳定性问题。同时随着端游黄金时代的结束,网站性能问题也逐渐显得不再重要。

在围绕新版本网站是否要正式替换老版本网站的问题上,各个利益方争执不下,反反复复拉锯战,而空降而来的女CTO不属于任何派系,态度模棱两可。最终斗争的结果.net利益方战胜了Java利益方,Java研发部门被清洗。

 

 点评:最后的结果是Java的架构也没有办法解决性能问题。

我的.net系统架构改造的经验和教训

批注:事前说明,我对.net不熟,对Java等其它技术很熟、很喜欢。

3年前,我刚接手CSDN的产品和研发团队的时候,CSDN的核心系统大约2/3是Windows平台.net架构,1/3是LAMP架构。研发人员当时也很少:只有2个.net程序员,3个PHP程序员,后来还有1个我带过来的Ruby程序员。当时的计划是:网站整体架构改造的方向是Linux架构,逐渐替换掉现有的.net系统。因此不打算继续招聘和补充.net程序员了,现有的.net程序员负责老的核心系统维护工作。

但硕果仅存的2个.net程序员在随后不到半年时间都走了:一个.net程序员跟着微软出来的高管去创业,另一个.net程序员跳槽去百度做了前端工程师。这中间的道理也很简单:既然公司要去.net化,那.net工程师就会担心等到将来.net系统都换掉之后,自己在公司还有价值吗,不就彻底边缘化了吗?

当然我在制订架构迁移计划的时候,也考虑到了这一点:我给那个更资深的.net工程师制订的是整个公司总架构师的培养计划,那个精通JS的.net工程师制订的是未来前端团队Leader的培养计划。不过有不确性的承诺总是不如现实的威胁更迫切,所以我也特别能够理解.net工程师的流失。

这个时候,我陷入了一个两难的处境:

  • 如果未来还是沿着“去.net化”的计划往下走,就算重新招聘和搭建了.net研发团队,由于.net在公司是注定要被替换掉的,那么新的.net团队是不可能稳定的,很可能来一两个月,一看形势不对,立马走人了。而当时.net的核心系统占整个网站的比重很高,且极端复杂,一旦出问题,根本就束手无策,必须要有好手坐镇维护。当时我已经开始review核心系统的.net代码,准备亲自上阵了。

  • 如果未来放弃“去.net化”的计划,也许短期可以解决一些头疼的系统维护的问题,但是对整个网站长期的发展会带来很多不利的方面:例如网站的安全性问题,网站的架构扩展问题,网站服务端软件全面正版化的成本问题等等。如果当时不下定决心去.net化,那么将来再想做这个事情,代价只会越来越高。

当时,我最初的想法是:招聘2名水平尚可,没有太大上进心,可以安于现状,踏踏实实工作的.net程序员来维护老的.net核心系统;同时招聘和搭建ruby研发团队,以我过去用ruby开发网站的惊人开发效率,争取时间,逐一重写老的.net核心系统。但是这样做,风险也很大:

  1. 我来CSDN的时间不是很长,当时CSDN线上产品又多又杂,足有上百个之多,很多系统我都不清楚干什么的;
  2. 公司领导也不太支持我这么快动手,甚至很担心我大刀阔斧的改造网站,会把当时已经很脆弱的网站彻底搞休克;
  3. 我来北京以后,只带过来1个Ruby程序员,而搭建Ruby团队,磨合团队,开发核心系统,都不是一朝一夕的事情,想快也很难快起来;

幸运的是,我招聘过程中,面试到了两个相当不错的.net工程师,有很强的上进心,编程功底和悟性都很好。虽然不符合我当时想找安于现状的工程师的标准,但我又不太想错过好的人才,所以我临时改变了自己的想法,将他们招过来,组建了新的.net团队。

为了避免出现之前.net团队流失的问题,给新的.net团队创造在公司发展的机会和空间,我想来想去,决定采取一个折衷的方案:即保留和沿用.net编程语言和框架,但是网站整体架构仍然去Windows化,概要说来:

  1. 数据层放弃SQL Server数据库和存储过程,全部迁移到Linux平台上的MySQL数据库上;
  2. 缓存不再依赖.net自身提供的缓存机制,迁移到部署在Linux平台上的分布式的Redis上;
  3. 服务之间的调用,避免使用.net自身专有协议,改成Restful的HTTP Web API调用;
  4. 静态资源请求,不再让IIS自己处理,分离到Linux平台上的nginx去处理;
  5. 需要读取的文件系统,也改成访问Linux平台上的分布式文件系统;
  6. 部署.net代码的Windows服务器放在LVS后面,用LVS做负载均衡和故障切换;

简单说来,就是单纯让.net做应用层的编程语言和框架,其他都交给Linux平台的开源解决方案。而.net框架单纯做应用层,无论ASP.net MVC的开发效率,还是.net CLR虚拟机的运行效率都非常好,目前我们单台Windows服务器上跑几百万的动态请求毫无压力,而且应用层架构是可以横向扩展的:如果请求负载非常高,只需要添加更多Windows服务器即可。总之,做到了扬长避短。

此外,我也比较注重不同编程语言研发团队之间的交流,鼓励.net工程师熟悉Linux操作系统,培养.net工程师整体架构意识。我们现在的主力.net骨干和我说,感觉来到这里以后技术上最大的提升就是视野一下被打开了。

在后来两年的整个网站改造过程中,也证明了这样的做法是相当成功的:

  1. .net团队稳定的延续了下来,而且成为整个研发部门表现一直非常突出的团队;
  2. 整个系统改造的过程非常稳健和平滑,没有碰到过什么风险;
  3. 对网站用户的冲击很小,基本上都是在潜移默化当中,不知不觉的完成了整个网站产品的翻新;
  4. 对公司线上业务也没有造成任何影响,而且随着系统不断改造,对业务的支持越来越好;

当网站架构全面Linux化,并且架构解决方案全部统一以后,其实在应用层用什么编程语言来写,就不是一件重要的事情了,我们目前应用层现有产品线,既有.net,也有PHP和Ruby写的,但是架构都是统一的,用什么编程语言,主要取决于研发团队资源的调配情况而定。

总之,以我的经验来说,一个传统上严重依赖微软解决方案架构的网站,如果要进行架构改造,迁移到Linux平台,甚至用其他编程语言重写,从来都不是一个单纯的技术问题,它至少涉及如下几个层面的问题:

  1. 如何保障旧系统的研发团队的利益问题,如何做到激励老团队参与架构改造,分享成功。这是最重要的,往往也是架构迁移最容易出现的致命问题,如果架构改造注定要牺牲老团队,完全不考虑和保障他们的利益,我认为一定会产生残酷的政治斗争,最终必然失败;
  2. 现有业务系统如何保持正常运转和平滑过渡的问题,如果架构改造影响到了业务,那一定会夭折;
  3. 如何保证网站用户体验的平滑过渡和改善的问题,如果架构改造影响了用户基本使用体验,那也一定会被叫停;
  4. 领导对架构改造当中出现风险的容忍度问题,以及领导对架构改造周期拉长以后的耐心问题;

一点题外话

我感觉我们互联网行业有一个不太好的现象:有些网站在促销期间瘫痪了,有些网站经常出现访问不稳定的现象,公司老板就喜欢跑到微博上来放狠话,请下属喝茶,或者急就章的嚷嚷百万年薪招CTO,这些都是很幼稚的做法。这就好比一个人,平常生活习惯差,花天酒地,从不注意养生,结果长年累月下来,终于病倒了。在这个时候狠狠的挥舞支票嚷嚷,哪个名医能给我药到病除,我给谁百万报酬。

所以,当一个网站出现严重的技术问题,其根源往往都不是单纯的技术问题,也不是单纯换个CTO就可以药到病除的,要反思公司企业文化是不是从来没有重视过研发,对技术团队的激励到位了吗?对架构师的意见重视过吗?对未来可能面临的技术门槛是否有过长期的研发投入?

关于这个现象,我记得Fenng说过一句很精辟的话:“技术问题,总是在短期被高估,在长期被低估”,我也想补充一句:“技术出现了问题,从来都不单纯是技术导致的问题”。

 

批注:“技术出现了问题,从来都不单纯是技术导致的问题,是CTO个人喜好的问题,或者说是CTO的能力水平问题

大端小端 说明

1.引入

  计算机的字长一直在增长,从4位开始,经过8、16、32、64位,以后可能还会有更多的位出现。现在的计算机还都是以字节(即8个bit位)作为地址编址单元

  编程语言中有许多内置的数据类型,有整型,浮点型等。每种内置类型所占用的字节数是不相同的。

  大端小端就是指在一个内置数据类型中,数据的存放顺序,分为两种,即小端优势和大端优势。

2.概念

  大端优势:数据的高字节存储在低地址中,而字数据的低字节则存放在高地址中

  小端优势:数据的低字节存储在低地址中,而字数据的高字节则存放在高地址中(所谓的“高高低低”)

3.C代码测试大端小端

  说明:C语言内置类型int默认是分配4个字节。从输出来看高地址输出的是数据的高字节部分,所以是小端优势。

复制代码
#include<stdio.h>
int main()
{
    unsigned int a=0x12345678;  //16进制数
    char *d=(char*)&a;

    printf("%x %x\n",d,*d);         //输出:22ff18 78
    printf("%x %x\n",d+1,*(d+1));   //输出:22ff19 56
    printf("%x %x\n",d+2,*(d+2));   //输出:22ff1a 34
    printf("%x %x\n",d+3,*(d+3));   //输出:22ff1b 12
    return 0;
}
复制代码

4.图示

  基于任务的异步模式_第4张图片

 5.再说明

   大端小端完全是设计的原因,不是因为有多个字节先写后写的问题。

   字长32位、64位的机器在CPU设计时,就可以使用32或64条数据线一次读或写多个字节数据。又因为可以一次读或写多个字节数据,有的RAM设计才有了地址对齐一说。

  

  

 
 
分类:  其他

你可能感兴趣的:(异步编程,断点续传,tap,基于任务的异步模式)