• 大话异步与并行(一)


    很久没安下心来写博客了,几年的开发过程中,对于异步与并行的了解也随着清淅起来。首先很多人问我,异步与并行的区别,那么我们来了解下概念。

    本博文写的主旨是用最白话的语言来说明问题,不想照搬概念。

    在古老的单核计算机中,一般是单核的,并行也只是在进程中交替的执行,表现出来的像并行执行一样,只是时间比较短,在多核处理器的计算机中,进程不仅可以交替执行,而且可以重叠执行,所以说,并行,只有在多核处理器中才有真正意义。很多人可能会突然不理解,并行与并发,是什么区别,并行,就像两种时刻相同的进程同一时刻运行,而并发不一定同一时刻运行,这就微妙的区别。

    就拿订单来说吧,在下单超大的情况下,A用户下了一个订单,还没有来及结束,B用户又下了一个订单。那么,这时最有可能发行的情况就是并发事件。

    那么我们今天重点说说异步,异步,是相对于同步来说的,我们知道,应用程序都是由一个主线程来运行的,这个主线程,是按照顺序来处理我们写的逻缉代码的,这就是同步,引用异步的好处是,在不打挠主线程的前提下,继续开放一个线程来指行其它的事情,就相当于,把部分工作交接给别人,当别人做好了后,然后,对我说,你交待的任务我已做好了。别人做好的工作交待给我的过程,相当于结果的返回中断。

    异步最终的目的就是给我们带来更高效的时间效应,它是一种结果,而实现这个异步的可能是异步委托,线程池,线程等等,只不过是一种方法或途径罢了,这就是线程与异步的最好诠释。

    下面来说说异步自然缺少不了多线程这个重头戏。

    实现多线程方式很多,下面一个一个的讲起。

    1.投票

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
     
    namespace ConsoleApplication1
    {
        class Program
        {
            public delegate decimal TakeDelegate(decimal data, int ms); 
       
            static void Main(string[] args) 
            {
     
                DateTime now = DateTime.Now;
     
                TakeDelegate dl = SaveBankAccount;
                IAsyncResult ar=  dl.BeginInvoke(1, 200, null, null);
     
                while(!ar.IsCompleted)
                {
                    Console.WriteLine("main thread wating current run at treadID:" + Thread.CurrentThread.ManagedThreadId);
                    Thread.Sleep(50);
                }
     
                decimal result = dl.EndInvoke(ar);
                Console.WriteLine("CurrentMoney:{0}", result);
                Console.WriteLine("runtime:{0}", (DateTime.Now-now).TotalSeconds);
                Console.WriteLine("main thread IsBackground " + Thread.CurrentThread.IsBackground);
                Console.ReadKey();
            }
       
            static decimal SaveBankAccount(decimal money,int ms)
            {
                Console.WriteLine("SaveBankAccount thread started! current run at treadID:" + Thread.CurrentThread.ManagedThreadId);
                Console.WriteLine("SaveBankAccount thread IsBackground " + Thread.CurrentThread.IsBackground);
                Thread.Sleep(ms);
                Console.WriteLine("SaveBankAccount thread completed!");
                return ++money;
            }
     
        }
    }

    一直都在想一个问题,为什么这种方式名字叫投票!百度 google都没有结果,最后想着想着也就想清楚了,所谓投票,就是对结果的一种猜测,“是否结束投票”


    2.异步回调

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
     
    namespace ConsoleApplication1
    {
        class Program
        {
            public delegate decimal TakeDelegate(decimal money,int ms);
            static DateTime now = DateTime.Now;
     
            static void Main(string[] args) 
            {
                TakeDelegate dl = SaveBankAccount;
                var ar = dl.BeginInvoke(1, 200, AsyncCallBack,dl);
     
                while(!ar.IsCompleted)
                {
                    Console.WriteLine("main thread wating current run at treadID:" + Thread.CurrentThread.ManagedThreadId);
                    Thread.Sleep(50);
                }
                Console.ReadKey();
            }
       
            static decimal SaveBankAccount(decimal money,int ms)
            {
                Console.WriteLine("SaveBankAccount thread started! current run at treadID:" + Thread.CurrentThread.ManagedThreadId);
                Console.WriteLine("SaveBankAccount thread IsBackground " + Thread.CurrentThread.IsBackground);
                Thread.Sleep(ms);
                Console.WriteLine("SaveBankAccount thread completed!");
                return ++money;
            }
     
            static void AsyncCallBack(IAsyncResult ar)
            {
                if (ar == null)
                {
                    throw new ArgumentNullException("ar");
                }
     
                TakeDelegate dl = ar.AsyncState as TakeDelegate;
     
                decimal result = dl.EndInvoke(ar);
                Console.WriteLine("CurrentMoney:{0}", result);
                Console.WriteLine("runtime:{0}", (DateTime.Now - now).TotalSeconds);
                Console.WriteLine("main thread IsBackground " + Thread.CurrentThread.IsBackground);
              
            }
        }
    }

    这个方法是在投票的基础,加入了回调函数而已。还有一种方法于投票差不多,就是等待句柄(AsyncWaitHandle),这个方法与投票没有太大的差异。没事的同学可以百度一下,这里就不多说了。

    如果说到这里,那么,我想微软也太失败了,因为这样玩异步太操心,那么多的代码。

    随着时间的推移,微软在.net3.0 C# 3.0的大包裹越来越健全 拉姆达(lambda)表达式与匿名方法 孕育而生。

    好吧,我们对上面的代码是时候要简化的必要了。

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApplication1
    {
        class Program
        {
    
    
            static void Main(string[] args)
            {
                DateTime now = DateTime.Now;
                Func<decimal, int, decimal> f = SaveBankAccount;
    
                var ar = f.BeginInvoke(1, 200, (r) =>
                {
                    if (r == null)
                    {
                        throw new ArgumentNullException("r");
                    }
                    Console.WriteLine("CurrentMoney:{0}", f.EndInvoke(r));
                    Console.WriteLine("runtime:{0}", (DateTime.Now - now).TotalSeconds);
                    Console.WriteLine("main thread IsBackground " + Thread.CurrentThread.IsBackground);
    
                }, null);
    
                while (!ar.IsCompleted)
                {
                    Console.WriteLine("main thread wating current run at treadID:" + Thread.CurrentThread.ManagedThreadId);
                    Thread.Sleep(50);
                }
                Console.ReadKey();
            }
    
            static decimal SaveBankAccount(decimal money, int ms)
            {
                Console.WriteLine("SaveBankAccount thread started! current run at treadID:" + Thread.CurrentThread.ManagedThreadId);
                Console.WriteLine("SaveBankAccount thread IsBackground " + Thread.CurrentThread.IsBackground);
                Thread.Sleep(ms);
                Console.WriteLine("SaveBankAccount thread completed!");
                return ++money;
            }
        }
    }
    复制代码

    再次狠狠的优化

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConsoleApplication1
    {
        class Program
        {
    
    
            static void Main(string[] args)
            {
                DateTime now = DateTime.Now;
                Func<decimal, int, decimal> f = (money, ms) =>
                {
                    Console.WriteLine("SaveBankAccount thread started! current run at treadID:" + Thread.CurrentThread.ManagedThreadId);
                    Console.WriteLine("SaveBankAccount thread IsBackground " + Thread.CurrentThread.IsBackground);
                    Thread.Sleep(ms);
                    Console.WriteLine("SaveBankAccount thread completed!");
                    return ++money;
                };
    
                var ar = f.BeginInvoke(1, 200, (r) =>
                {
                    if (r == null)
                    {
                        throw new ArgumentNullException("r");
                    }
                    Console.WriteLine("CurrentMoney:{0}", f.EndInvoke(r));
                    Console.WriteLine("runtime:{0}", (DateTime.Now - now).TotalSeconds);
                    Console.WriteLine("main thread IsBackground " + Thread.CurrentThread.IsBackground);
    
                }, null);
    
                while (!ar.IsCompleted)
                {
                    Console.WriteLine("main thread wating current run at treadID:" + Thread.CurrentThread.ManagedThreadId);
                    Thread.Sleep(50);
                }
                Console.ReadKey();
            }
    
        }
    }
    复制代码

    着重看下两个划框的部分,lambda表达式采用匿名方法成功的简化了传统的方式,里面的参数获取,将来的更为便捷,所以四个参数后,用了null。

    public delegate TResult Func<in T, out TResult>(T arg) 委托 是微软在.net 3.5 再次对delegate的封装,第一个参数是输入参数,第二个是返回参数,此时,我们不用太辛苦的到处声明委托,这点微软也给我们省了,不得不说,代码的优美不是java能比的。

    细心的朋友可能看到上面一段代码 IsBackground 这个时候,表示线程是前台线程还是后台线程。

    前台线程与后台线程的区别:

    就像word文档一样,打开word 即开启了word主线程即前台线程,诸如 语法检查属于后台线程,仔细想想,这样设计,还是有道理的,当关闭了word前台主线程,后台线程语法检查也没有必要了。

    一个进程里必须有一个前台线程,不一定有后台线程。当前台线程已结束的时候,后台线程也将结束。

    看下面代码

     

    通常,应该将被动侦听活动的线程设置为后台线程,而将负责发送数据的线程设置为前台线程,这样,在所有的数据发送完毕之前该线程不会被终止。只有在确认线程被系统随意终止没有不利影响时,才应该使用后台线程。如果线程正在执行必须完成的敏感操作或事务操作,或者需要控制关闭线程的方式以便释放重要资源,则使用前台线程。

    例如 《C#高级编程》中有个例子--如果关闭Word程序,拼写检查器还在运行其进程就没有意义了。在关闭应用程序时拼写检查器线程就可以关闭。

     小结:本节只是对于基础知识线程与异步委托作了个简单的复习,让我联想到,主线程也好,新开的线程也好,无非都是线程的部分,线程更多的是一种方法,而异步是一个需要线程支撑的结果,所以可以在任何线程上开启异步的操作,因为主线程都可以开启异步嘛。

  • 相关阅读:
    bash命令
    集群解析
    临时配置网络(ip,网关,dns)+永久配置
    nginx反向代理三台web
    源码安装nginx
    nginx反向代理+三台web+nfs共享存储实现集群配置
    jQuery学习笔记(一):入门【转】
    你是怎么把字符串“2016-11-16” 变为 “16/11/2016” 的? 【转】
    用css画出三角形【转】
    JS中常遇到的浏览器兼容问题和解决方法【转】
  • 原文地址:https://www.cnblogs.com/zhangxiaolei521/p/5893450.html
Copyright © 2020-2023  润新知