各语言并发问题_哪些语言支持高并发-程序员宅基地

技术标签: 并发  c/c++  

为什么需要并发?

曾有一段黄金时间,每18个月时钟速度就会增加一倍。如果程序不够快,那程序员只要等一等,计算机就会追上来了。

那个时代太美好,然而却一去不复返了。CPU设计者们通过向计算机增加更多核心的方式试图跟上摩尔定律。

这就造成了一个问题,这个问题被淹没在营销的辞藻中,而大多数程序员都没领会它的含义。在新的世界中,我们的程序依然能够每18个月提高一倍速度,但前提就是有效通过并行程序使用多个内核。

因此,作为程序员,在并行环境下写代码的能力是个核心技能。这篇文章介绍了各种语言对并行和并发程序的支持情况。

经典并发原语

几乎所有的操作系统都支持多线程执行,但并发程序员还需要解决另外两个问题:

  • 共享的数据。如果并发访问共享的数据,可能会产生无法预料的结果;
  • 线程间的信号传递。有时程序员需要控制线程的执行顺序,一个例子就是程序员要让线程在某个点等待另一个线程,让它们按顺序执行,不要超过另一个线程,或者某个关键区域最多只能进入N个线程等。

编程语言提供了各种原语来辅助程序员控制以上的情况,下面是一些经典的原语:

  • 锁(Lock,也叫作互斥锁,Mutex):保证只有一个线程能进入指定的代码区域;
  • 监视器:功能和锁一样,但比锁好一些,因为使用锁时必须要解锁;
  • (计数的)信号量(Semaphore):一种强大的抽象概念,能支持多种协同场景;
  • 等待并通知:功能相同,但比信号量弱一些,因为程序员必须在等待之前处理丢失的通知触发;
  • 条件变量:当某个条件触发时让线程睡眠或唤醒;
  • 带有条件等待的通道和缓冲区:如果没有线程接收信息的话,则监听并收集信息(可以选择有边界的缓冲区);
  • 非阻塞数据结构(如非阻塞队列、原子计数器等):这些智能数据结构支持从多个数据结构中访问,而无需使用锁,或者将锁的使用控制在最少。

这些原语的功能有重叠。任何编程语言只需要几个原语就能得到并发的全部力量。例如,锁和信号量就能完成你能想到的任何并发场景。

原语的语言支持

选择并发原语并不是依据它们的功能。不同的原语适用于不同的编程模型,它们对应问题的不同思考方式。不同的编程模型选择了不同的原语集,以适合各自的编程模型。选择哪一种取决于设计者的口味和语言的哲学。

我们来看看都有哪些选择。

Java和C#

Java和C#的选择就是没有选择,两者都支持所有原语。

Java最初只支持监视器(synchronized关键字)和等待并通知,结果发现线程间的信号传递是个噩梦。我还记得我曾在“信号丢失”的问题上花了数个小时,最后依然无法得到正确的结果。

不久Java的设计者意识到这个错误,于是增加了concurrency包,支持所有原语,包括非阻塞数据结构。

唯一没有原样支持的原语就是通道和缓冲区。但是,如果你有需求,很容易用队列和缓冲区进行模拟,当然你自己的实现绝对赶不上Go或Erlang的性能。

后起之秀C#从Java学了不少东西,它也支持所有原语。它还比Java多了几个高阶辅助结构,用于解决常见的问题,如barrier等。

更具体的内容请参见C# threading package:

https://msdn.microsoft.com/en-us/library/system.threading%28v=vs.110%29.aspx。

C和C++

C最初依靠系统调用实现多线程,结果就是牺牲了可移植性。于是,第三方并发库出现了。不幸的是,由于语言并没有规定API,因此许多库都实现了不同的API。因为C和C++是最接近操作系统的语言,最前沿的线程研究经常在这两种语言上进行。

例如,简单搜索下就能发现22个C++并发库和6个C并发库:

https://en.wikipedia.org/wiki/List_of_C%2B%2B_multi-threading_libraries;

https://stackoverflow.com/questions/5613646/threading-in-c-cross-platform。

它们不缺乏力量,所有这些库都包含了广泛的、最尖端的技术。但是,由于API多种多样,因此熟知某个特定API的程序员寥寥无几。

Erlang

Erlang天生就是为并发设计的。Erlang以消息传递的方式为程序员提供了对进程间交互的完全控制,程序员必须负责所有通信。这就是Erlang在多核计算机上能达到高性能的原因。

但是,这样做是有代价的。Erlang不支持线程间的状态共享,因为共享的状态会导致线程间同步,这种同步不由程序员直接控制,而且经常会导致性能降低。

其结果就是,用Erlang编程对于多数程序员来说就像外星语一样。尽管它完全是函数式的,也无济于事。

Erlang中的主要并发结构就是通道。它内置了缓冲区,而且支持在条件上等待。例如,可以要求通道等待,直到接收了满足给定条件的消息。每个进程都有一个通道,并且只能从那个通道扫接收消息。

在实际应用中,由于Erlang被设计成函数式编程语言,因此基本上不需要共享内存锁。但不幸的是,实际中经常存在这种情况。由于Erlang没有共享内存,因此没办法锁任何东西。但是,可以创建一个进程来代替锁,像分布式系统那样,通过给该进程发送消息执行加锁和解锁的操作。

除非你是个熟知函数是编程的计算机语言专家,否则写出的程序通常会极其复杂,并且难以调试。选择Erlang就是用易用性换取了并发的支持。

如果希望了解更多,可以阅读《Erlang for Concurrent Programming》和《The Hitchhiker's Guide to Concurrency》:

https://queue.acm.org/detail.cfm?id=1454463

http://learnyousomeerlang.com/the-hitchhikers-guide-to-concurrency

Go

Go很像Erlang,它主要的并发模型就是通道和缓冲区,并且支持条件等待(https://www.golang-book.com/books/intro/10)。其核心思想是:不要用共享内存进行通信,应该用通信来共享内存(https://golang.org/doc/effective_go.html#sharing)。

但是有个本质的区别,Go信任你会做正确的事情。Go允许你在线程间共享数据,同时还支持互斥锁(https://gobyexample.com/mutexes)和信号量(https://github.com/golang/sync/blob/master/semaphore/semaphore.go)。此外,它还放宽了Erlang对于每个通道永久绑定到一个线程的限制,程序员可以随意创建通道并随意传递。

总之,Go希望程序员像使用Erlang那样使用并发。但是,Erlang会做出强制,但Go相信你会写对。如果Erlang代表了独裁国家,Go就代表了自由州。

RUST

Rust也很像Erlang和Go。它使用通道进行通信,支持缓冲区,支持条件等待。与Go相似,它也放宽了Erlang的限制,允许使用共享内存(https://doc.rust-lang.org/book/second-edition/ch16-03-shared-state.html),支持原子级别的引用计数和锁,并且允许将通道从一个线程传递到另一个线程。

但是Rust前进了一步。Go会完全信任程序员,而Rust会给程序员指派一名导师,如果程序员写错,导师就会提出警告。Rust的导师就是编译器,它会做复杂的分析,确定线程间传递的值的所有者,并在可能出现问题时发出编译错误。

下面的话引自RUST文档。

所有者规则在消息传递中扮演了重要的角色,因为它能帮我们编写安全的并发代码。使用Rust语言,就必须要付出考虑所有者的代价,但换来的好处就是能在并发编程中防止错误——通过值的所有者进行消息传递。

如果Erlang是独裁国家,Go是自由州,那么Rust就是保姆州。

调试并发程序是个噩梦,运气差时需要花上好几天的时间,所以Rust能在编译器级别进行分析是非常有帮助的。

但是,如果你没有并发编程经验,却想用Rust编写并发程序,它就非常讨厌了。不管你做什么,它都会用艰涩的言语提醒你并发,改了之后它又会抱怨别的,周而复始。你不得不完全理解并发,在那之前用Rust编程可不太容易。

相反,Go模型把安全的责任交给了程序员,程序员通常会(错误地)认为他的做法是正确的,但以后他就会付出代价。但是,只有当代码进入生产环境,只有当用于遇到那种极端情况,并且错误被检测到,检测到的错误还被反馈给同一个程序员时,他才会付出代价。这里面的“只有”太多了。尽管这并不公平,但多数情况下那个程序员早就离职了。人类并不喜欢迟来的满足感和长期规划,所以程序员喜欢Go胜于Rust。

Rust想要帮忙,但几乎没人感谢它。没人喜欢保姆。

更多的内容请阅读《Rust和Go的并发原语比较》:

https://news.ycombinator.com/item?id=7851274

结论

谈起并发的哲学时,不同的编程语言给你不同的选择:自由州(Go),独裁王国(Erlang),或保姆州(Rust)。

如果你希望了解更多内容,我可以推荐两个资源。首先,阅读《浅谈信号量》(http://greenteapress.com/wp/semaphores/),它会教给你关于锁和信号量的一切。其次,如果想理解通道和Erlang模型,可以看看MPI(http://mpitutorial.com/)。你可能认为MPI是个死亡的语言,其实不是,今天许多科学模拟依然在使用MPI,天气预报、汽车设计、新药的发现都离不开它。科学就是由MPI推动的,MPI对并发的用法超出了我们的想像。

想尝试一下的话,可以看看MPI通信原语:

http://www.mathcs.emory.edu/~cheung/Courses/355/Syllabus/92-MPI/group-comm.html

读完上面两个材料后,就可以理解并发的复杂性和可能性了,并发则需要一生的时间去精通。

原文:https://medium.com/@srinathperera/concurrency-ideologies-of-programming-languages-java-c-c-c-go-and-rust-bd4671d943f

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/liujiandu101/article/details/81101254

智能推荐

数据库Mysql+SSM技术开发,SSM 泰州市二手房交易平台系统--03401(上万套实战教程手把手教学,免费领取源码)-程序员宅基地

文章浏览阅读162次。关注●点赞收藏并私信博主,免费领取项目源码哦~总体设计主要包括系统总体结构设计、系统数据结构设计、系统功能设计和系统安全设计等;详细设计主要包括模块实现的关键代码,系统数据库访问和主要功能模块的具体实现等。最后对系统进行功能测试,并对测试结果进行分析总结,及时改进系统中存在的不足,为以后的系统维护提供了方便,也为今后开发类似系统提供了借鉴和帮助。

【部署网站】使用nginx+tomcat部署博客网站_用nignx发布网站和用tomcat部署-程序员宅基地

文章浏览阅读3.1k次,点赞3次,收藏16次。一、什么是静态网站、动态网站?静态网站没有采用任何程序开发,是纯粹使用html语言写出的网站,网页文件名以html或htm结尾。原则上不会受到攻击入侵,但是也无法在网络上实时更新内容,就纯粹的是制作好的页面。动态网站目前的主要开发语言有ASP,JSP,PHP,ASP.NET在制作好之后,都有一个网站管理后台,当以管理员身份登陆时,可以对网站的内容进行增删操作,直接在网上进行这些操作,虽然它可以随时更新,但是速度较慢。并且需要区分的是,动态网站的动态指的是动态实时更新而非网站有动态画面。区分静态网站和动_用nignx发布网站和用tomcat部署

android 实现定时任务,Android 实现定时任务的五种方式的讲解-程序员宅基地

文章浏览阅读3.9k次。1、普通线程sleep的方式,可用于一般的轮询Pollingnew Thread(new Runnable() { @Override public void run() { while (true) { //todo ..._android 定时20个小时

Dr_can模型预测控制笔记与代码实现-程序员宅基地

文章浏览阅读2.7w次,点赞206次,收藏552次。因而我们引入模型预测控制(Model PredictiveControl)的概念,对于一般的离散化系统(因为实际计算机实现的控制系统都是离散的系统,连续系统离散化的方法在此不述)。在k时刻,我们可以测量或估计出系统的当前状态y(k),再通过计算得到的u(k),u(k+1),u(k+2)...u(k+j)得到系统未来状态的估计值y(k+1),y(k+2)...y(k+j);我们将预测估计的部分称为预测区间(Predictive Horizon),将控制估计的部分称为控制区间(Control Horizon)_dr_can

由浅入深!小程序FMP优化实录,已拿offer入职_小程序fmp是指的什么(1)-程序员宅基地

文章浏览阅读569次,点赞25次,收藏12次。其实很简单就下面这张图,含概了Android所有需要学的知识点,一共8大板块:架构师筑基必备技能Android框架体系架构(高级UI+FrameWork源码)360°Androidapp全方位性能调优设计思想解读开源框架NDK模块开发移动架构师专题项目实战环节移动架构师不可不学习微信小程序混合开发的flutterAndroid学习的资料我呢,把上面八大板块的分支都系统的做了一份学习系统的资料和视频,大概就下面这些,我就不全部写出来了,不然太长了影响大家的阅读。

计算带余除法(四种方法)_带余除法怎么写编程-程序员宅基地

文章浏览阅读387次,点赞12次,收藏4次。给定两个整数a和b (0 < a,b < 10,000),计算a除以b的整数商和余数。一行,包括两个整数a和b,依次为被除数和除数(不为零),中间用空格隔开。一行,包含两个整数,依次为整数商和余数,中间用一个空格隔开。示例:输入:15 2,输出:7 1_带余除法怎么写编程

随便推点

数字化转型背景下的金融交易业务中台实践-程序员宅基地

文章浏览阅读140次。引言:目前金融业IT系统大多由业务部门或渠道进行竖井式建设,这种模式的好处是系统专业性强,但同时也给运营及IT管理部门带来分散性阵痛。那么如何在强监管与统一风控的形势下,实现统一管控、快速响应、应需而变、按期交付?中台架构就是在这种背景下应运而生。本文主要以某城商行基于BIIP实施的交易中台的实践案例展开分享,一起和大家探讨企业数字化转型中的背景、技术..._运营转型 业务中台

AWG标准_awg官方规范-程序员宅基地

文章浏览阅读1.5k次。AWG 直径 面积 铜阻抗 (inch) (mm) (kcmil) (mm²) (Ω/km) (Ω/kFT) 0000 (4/0) 0.46 11.684 212 107 0.1608 0.04901 000 (3/0) 0.4096 10.404 168 85 0.202..._awg官方规范

图像修复论文Residual Non-local Attention Networks for Image Restoration阅读笔记-程序员宅基地

文章浏览阅读2.8k次。论文来源:ICLR2019论文链接:pdf (openreview.net)_residual non-local attention networks for image restoration

表达式计算。问题描述:编写程序,计算并输出如下表达式的值:y=其中a,x,y均为float类型,取值为3.1415926。输出结果要求保留小数点后3位。_serialprintln(a)的结果为-程序员宅基地

文章浏览阅读153次。【代码】表达式计算。问题描述:编写程序,计算并输出如下表达式的值:y=其中a,x,y均为float类型,取值为3.1415926。输出结果要求保留小数点后3位。_serialprintln(a)的结果为

android 自定义Toast 吐司-程序员宅基地

文章浏览阅读274次,点赞3次,收藏8次。【Android 详细知识点思维脑图(技能树)】其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

FP-Growth算法之FP-tree的构造(python)_利用fpgrowth算法对其构造一个fptree,树的最大高度-程序员宅基地

文章浏览阅读7.4k次。前言:关于 FP-Growth 算法介绍请见:FP-Growth算法的介绍。 本文主要介绍 FP-tree 的构造算法,关于伪代码请查看上面的文章。上接:FP-Growth算法python实现;下接:FP-Growth算法之频繁项集的挖掘(python)。 正文:tree_builder.py\color{aqua}{tree\_builder.py}文件:#coding=utf-8import_利用fpgrowth算法对其构造一个fptree,树的最大高度