编程语言模型思考之异步和多线程

2 篇文章 0 订阅
1 篇文章 0 订阅

编程语言模型思考之异步和多线程

  • 各种类型的编程语言在解决特定领域问题上具有独有的编程模型,例如异步模型和多线程模型,语言最初设计者考虑哪种模型至关重要
  • 目的:不管哪种模型,其目的是为了程序的运行可持续性,也就是多任务执行解决方案。不能因为某些任务一直阻塞,而其资源(主要CPU)处于不工作状态,因此衡量一
    个语言的优劣,大部分评论文章针对是否充分利用处理器,以及对于编程人员在该模型上编程的易用性
  • 两种模型的比较
    • 异步模型:不论是操作系统还是语言运行库所提供的异步模型,为了不阻塞程序运行,对于一些耗时不确定的响应比较长时的操作可注册一个回调,为了管理这些回调
      处理完后的通知,一般语言运行库(比如js)提供事件编程模型,或者操作系统(异步IO)直接支持,其背后原理是运行事件的任务所在进程或线程提供调度策略,轮询
      频率以及任务优先级都交由事件调度完成。因为这种情况下不会阻塞,所以理论上一个逻辑控制流(一个cpu核充分利用)足已,而且对于资源的访问由于是单个的顺序访问
      ,因此很适合对于资源操作建模,不如js对于dom的操作,java swing对于UI的双事件机制,redis对于内存的控制。其缺点是代码层面的可读性,要面对回调地狱问题,js
      常见很多层回调,java swing监听内部类的化会更复杂,由于纯面向对象编程,用一个回调函数非得封装成类来传入,导致好多代码,只不过java其他地方没有那么多回调
      需求而已,庆幸的是这类语言会陆续更新一些高级的语法糖,java8推出一系列面向函数式编程的特性:lambda表达式,高阶函数,方法引用,FutureCompletable异步等
      来全面拥抱新的编程生态。js 的promise以及c#最先推出的async/await可谓是回调天堂。随着多核硬件的技术发展,这种模型已不太能充分利用硬件优势,有些模型会
      采取迂回措施,比如redis可以启动多个进程,资源控制用slot编号控制,Python解释器由于设计时有GIL全局锁,导致了多线程无法利用多核。多线程的并发在Python中
      就是一个美丽的梦。
    • 多线程模型:其实我这里表达的不太准确,应该是逻辑控制流不在是上述一条,而是至少两条以上,也就是并发,多线程只是并发的其一种解决方案。操作系统在单核cpu
      上通过时间片轮换来实现,此处不考虑时间片轮换实现的进程,因为进程本质上资源隔离,不在此讨论范畴。同样为了多任务不阻塞,充分利用资源(cpu),在该模型下
      将耗时操作新开一个线程,主调方程序完全以同步代码执行且无阻塞,可是新开线程的状态管理成了很大问题,甚至不可控,java 实现runable()接口的新线程其返回值
      和其中的异常都丢失,当然可以用callable()改善。线程间通信可通过共享对象完成,因此有及其复杂的同步控制还有各种锁来达到程序出错,还有一种解决方案,
      这些共享对象在web编程中大多数都已经持久化,其复杂的资源控制转移到数据库管理。只有java编程高手才能轻松驾驭这些模型。那这种复杂编程模型换来的优势就是
      在多核cpu或者超线程(一个核提供两并行逻辑流)上能物尽其用,能充分发挥硬件能力,因此后端语言大多选用此模型。随着语言的更新,java推出的线程池其在调度策略
      上的不可控(系统级线程实现,调度由操作提供)有所改善,防止无用线程的空转,其ForkJoin池更是能将任务几乎平均在各个线程中并行。由于线程创建所需资源依然很大,
      而线程池的解决方案对于编程人员来说依然很复杂。于是协程(可看做在线程上的子线程)应用而生,大多数程序执行时通过线程栈压栈出栈操作,本地方法栈一经弹出,
      其上下文立马摧毁,协程切换必须要实现这种能保存上下文环境的方法,以便之后恢复,并在语言层运行库上支持调度,python js 的Generator/yield就提供这种支持,
       当然支持闭包的语言其实在函数栈弹出后也会保存环境(函数式语言的coniuenation不只有返回值,任意时间点都能保存),go语言便是将协程亲民化的最好实现,
      协程与线程的比例为M:N,只需要go关键字就可以轻松的开启新的协程,其channel更是轻松的实现协程间的通信,及其易于编程服务器高并发程序。由于操作系统提供线程
      调度,那共享同一个线程的所有协程也共享同样的调度策略,一般协程携带的代码片段可看成在一些队列里,新开线程或者重用空闲线程来执行他们,但是这种协作式调度依然
      对于资源的控制还是要加锁理论上不如直接的事件驱动+合理的线程池 来的快。go的goroutine目的是简化代码逻辑,而不是使程序运行的更快,随着其运行库的不断发展和完善,
      其性能一定会越来越好,尤其是在CPU核数越来越多的未来,终有一天我们会为了代码的简洁和可维护性而放弃那一点点性能的差别。
    • 后记:并发编程就这样结束了吗?上述是针对cpu有限,尽可能挖掘其利用率的解决方案,对于目前分布式下动不动成千上万节点的并发又该如何处理?
      Akka框架是自管理+消息通信,自管理意味着各个节点自己处理自己的收到的消息,然后通过发送消息来通信,或者新开一个子节点,各个父节点管理各自的子节点,
      当某些节点挂掉后重建节点,消息不可达时重复发送消息。这就要求代码片段易于分割重建以及不变性,于是函数式编程大行其道,这种情况下通过消息通信的机制来避免
      竞态条件而不再是精确控制资源。在面向对象里调用方控制着被调用的对象,Actor这种模式只管与消息的通信,解决了很多并发
      的难点,正如Akka官方所说We believe that writing correct concurrent, fault-tolerantand scalable applications is too hard.
      Most of the time it’s because we are using the wrong tools and the wrong level of abstraction. —— Akka,
      之所以并发这么难,就是用了错误的抽象。随着廉价的硬件资源增多,软件解决方案又再次发生改变。这种模式已经大量运用在容器以及微服务管理上,以前的并发控制单一
      资源,现在都是复制副本以便可以创建新的节点,用廉价易得的空间换取着宝贵的时间。
    • 举个栗子:公司晨会后,领导给每个人下达任务,并不等一个人完成后在去给其他人下达,而是我们都说OK后,领导干其他事去了,等我们干完活了再通知领导
      做出下一步指使,领导异步工作。后来人员增多,需要再增加一个二领导同时协助下达任务,两个领导并行工作。再到后来公司规模无限大,领导也没精力亲力亲为下达
      任务了,每个人每天只处理自己受到的邮件任务,这便是第三种模式。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值