并发编程模型小结

做者:不洗碗工做室 - Markluxnode

出处:Marklux's Pub程序员

版权归做者全部,转载请注明出处编程

因为接触过的语言比较多,各类语言之间对并发任务的处理方式不尽相同,时常会使人迷惑。此次尝试系统的整理和总结一下常见的并发调度方式和编程模型。缓存

使用的线程数

《并发之痛》中认为,并发编程最难解决的问题是,究竟要建立多少个线程(这里指内核线程)才最合适。不一样的语言对于这个问题的理解和解决方案都不一样,但大体能够从用户线程和内核线程的比值上作一个分类多线程

用户线程:内核线程 = 1:1

参考下图:并发

一个使用编程语言建立的线程完整的映射到一个内核线程上去,Java就是这种实现。这么作线程的调度将主要依赖于操做系统自己的调度,若是建立的线程比较多,可能会由于上下文的切换问题致使性能低下。框架

这么作的好处在于,开发人员至关于直接在控制内核线程。经过合理的编程(如使用线程池),理论上能够最大限度的利用CPU性能。异步

缺点则是线程的建立和销毁成本比较高,同时编程模型也相对复杂一些,很差掌握。编程语言

用户线程:内核线程 = M:N

便是说,用户线程能够映射到任意一个内核线程上去。这种方式最为灵活,但编程语言自己的调度器实现难度会比较复杂一些,并且开发人员在开发时也就没法再直接去调度内核线程了。Go应该是目前为止实现M:N映射的语言中性能最好的一个。分布式

Go的映射方案以下图:M为系统线程,P为调度器,G为routine也就是用户线程。

当内核线程数量 = CPU核心数的时候,这种映射模型能够很轻松的使程序的CPU利用率达到一个很高的水平。

优点就是用户线程能够变的很轻量,建立销毁和调度的成本都很低,大部分状况下也没必要要刻意去维护线程池来提高性能了,编程难度下降了很多。

至于缺点就仁者见仁智者见智了,Go的调度器性能并非最优的,也不能保证全部场景下的高效率,遇到这类场合可能还要在代码上下一番功夫。

用户线程:内核线程 = N:1

以下图

严格意义上这种映射不算完整的并发,由于没法彻底利用多核的性能优点,并且任务也不能实现并行。

在脚本语言中用的比较多,如今最出名的应该就是JS了,并且恰恰使用了单线程的node性能还不差,所以如今也有很多人推崇这套方案。

编程模型上基本所有须要异步来实现,若是对异步编程方法了解的不够透彻写起来可能会比较头疼。

线程间通讯的方法

引入多线程后,线程和线程之间的资源竞争和执行顺序依赖都成为使人头疼的问题,这时就必须让线程之间能够互相通讯才能解决,而通讯机制的编程模型不一样,也会很大程度上影响程序员编写并发代码时的思路。

Lock共享资源

最基本的一种解决方式,就是经过多个线程同时读写某个变量的方法来实现。这么作很直观,不过若是锁使用不当,颇有可能会形成严重的性能损耗,更不要说死锁这类的状况了。

因为很是常见,因此不加以赘述了,主要重点放在下面两种方式上:

CSP线程通讯

传统的锁机制并很差驾驭,这时候CSP(Communicating Sequential Process)出现了,它的原则是“经过通讯来共享内存,而不是用共享内存来通讯”。

所以CSP模型里把通讯方式抽象成了管道(channel),全部线程间的通讯都依赖于管道的读写来实现。

相比于单纯的内存锁,管道对内存的读写者分别提供了各自的等待队列,而且封装了调度的逻辑,这样就简化了多线程通讯的复杂度,只要合理使用channel,就能够解决至关一部分的问题。

而go还为channel添加了缓存空间,使得channel的通讯方式从单一的同步阻塞,变得能够在必定数量上支持异步。

CSP最大的亮点在于只关心消息的传输方式,而把消息的发送者和读写者给解耦了,这背后蕴含的逻辑是“读写者很是关心本身的数据有没有被处理”,若是不是这样的场景,可能使用CSP模型带来的改观就不是很大了。

Actor模型

这个模型相对来讲比较新颖一些。目前已知的是Scala中有较大规模的应用。

和CSP不一样,Actor把重点放在消息的处理单元而不是消息的传输方式上,发送者和读写者都必须事先知道对方是谁,才可以进行消息的收发。

因为没有完整的使用过Actor模型,在这里引用一下别人总结的对于Actor模型的优点:

  • Actor可独立更新,实现热升级。由于Actor互相之间没有直接的耦合,是相对独立的实体,可能实现热升级。
  • 无缝弥合本地和远程调用 由于Actor使用基于消息的通信机制,不管是和本地的Actor,仍是远程Actor交互,都是经过消息,这样就弥合了本地和远程的差别。
  • 容错 Actor之间的通讯是异步的,发送方只管发送,不关心超时以及错误,这些都由框架层和独立的错误处理机制接管。
  • 易扩展,自然分布式 由于Actor的通讯机制弥合了本地和远程调用,本地Actor处理不过来的时候,能够在远程节点上启动Actor而后转发消息过去。

相关参考:并发之痛

相关文章
相关标签/搜索