为什么需要并发?
曾有一段黄金时间,每18个月时钟速度就会增加一倍。如果程序不够快,那程序员只要等一等,计算机就会追上来了。
那个时代太美好,然而却一去不复返了。CPU设计者们通过向计算机增加更多核心的方式试图跟上摩尔定律。
这就造成了一个问题,这个问题被淹没在营销的辞藻中,而大多数程序员都没领会它的含义。在新的世界中,我们的程序依然能够每18个月提高一倍速度,但前提就是有效通过并行程序使用多个内核。
因此,作为程序员,在并行环境下写代码的能力是个核心技能。这篇文章介绍了各种语言对并行和并发程序的支持情况。
经典并发原语
几乎所有的操作系统都支持多线程执行,但并发程序员还需要解决另外两个问题:
编程语言提供了各种原语来辅助程序员控制以上的情况,下面是一些经典的原语:
这些原语的功能有重叠。任何编程语言只需要几个原语就能得到并发的全部力量。例如,锁和信号量就能完成你能想到的任何并发场景。
原语的语言支持
选择并发原语并不是依据它们的功能。不同的原语适用于不同的编程模型,它们对应问题的不同思考方式。不同的编程模型选择了不同的原语集,以适合各自的编程模型。选择哪一种取决于设计者的口味和语言的哲学。
我们来看看都有哪些选择。
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
文章浏览阅读1.2k次,点赞31次,收藏26次。增强现实通常被视为一个利基领域。然而,在过去的两年里,它已经到了一个成熟的阶段,应该在一般的营销堆栈中进行考虑。正如我们所看到的,这个市场是巨大的,而且随着主要参与者向这项技术投入大量投资,它只会继续增长。从苹果到Meta,大公司都相信身临其境的未来,而想要获得成功的营销人员和创意人员也加入了进来。本文第三章,最佳设计实践除了深入讨论AR设计的原则外,还全面推荐了AI设计工具。旨在帮助读者的AI作品脱颖而出。
文章浏览阅读473次。OSI七层网络模型由下至上为1至7层,分别为:物理层(Physical layer),数据链路层(Data link layer),网络层(Network layer),传输层(Transport layer),会话层(Session layer),表示层(Presentation layer),应用层(Application layer)。1.1 应用层,很简单,就是应用程序。这一层负责_usage: ./tcp_client hostname
文章浏览阅读2.8k次。环境准备安装 maven 安装 java 环境[root@cicd-nexus ~]# wget http://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz[root@cicd-nexus ~]# tar xf apache-maven-3.6.3-bin.tar.gz -C /usr/local/[root@cicd-nexus ~]# tar xf _maximum component age
文章浏览阅读1.6k次。使用js-xlsx handsontable 可以把本地excel 解析到网页上,然后分批次传入后台。在chrome 下 可以参考 https://github.com/SheetJS/js-xlsx 【Browser file upload form element】但需要使用FileReader api 这个只有ie10 才开始支持。兼容ie9 ,ie9需要通过flash 来支持..._js 导excel 分批写入
文章浏览阅读2.5k次。在wxWidgets中,想要通过其本身的控件来实现图片+文件的按钮,貌似不太容易做到。但是可以通过重载wxControl来自绘图片+文件按钮。下面给出的是已经封装好的按钮类:wxBitmapButtonEx.h#ifndef _BITMAP_BUTTON_EX_H#define _BITMAP_BUTTON_EX_H#include "wx/wx.h"enum eBitm_wxwidgets 中文按钮
文章浏览阅读828次,点赞15次,收藏12次。首先,我们从一个问题开始( ̄∇ ̄)/为什么需要程序员?早期的计算机程序是硬件化的,即使用各种门电路组装出一个固定的电路板,这个电路板只能用于执行某个特定的程序,如果需要修改程序功能,就要新组装一个电路板。而一位叫做冯诺依曼的老爷子的出现,提出了一种指令数据化的思想,使得程序和数据一样,可以存储起来,组合调用,至此,程序脱离了电路板的桎梏,成为了可以人为组合的指令集,程序员应运而生。当然,我们需要郑重地介绍下这位冯老爷子,1903年12月28日—1957年2月8日),原名),出生于的籍与的奠基者,在和。_栈式指令集与冯诺依曼
文章浏览阅读847次。invalidate()与postInvalidate()都用于刷新View,主要区别是invalidate()在主线程中调用,若在子线程中使用需要配合handler;而postInvalidate()可在子线程中直接调用。postInvalidate它是向主线程发送个Message,然后handleMessage时,调用了invalidate()函数。(系统帮我们 写好了 Handle..._postinvalidate和invalidate的区别
文章浏览阅读9.1k次。excel数据进行分类汇总的步骤在做分类汇总前,我们需要对数据先进行排序,否则分类汇总无法进行。得到排序后的表格。点击上方工具栏中的“数据”→“分类汇总”。在弹出的对话框中选择“分类字段”→“汇总方式”→“决定汇总项”。点击确定出现数据汇总结果。Excel表格中求差函数公式使用的方法第一步:打开Excel表格,单击第一排,第三个“单元格”,也就是C1,在C1中输入“=A1-B1”;第二步:这个公式..._表格求差公式
文章浏览阅读1.5w次,点赞49次,收藏169次。前言好激动,断断续续装了两三天才装上,踩了好多坑。这里把成功安装的步骤详细写下来,如果有小伙伴需要,可以尝试一下,但我不能保证你也可以装好。首先说一下我的各个版本(不谈版本的安装教程都是耍流氓!)是用虚拟机软件:VirtualBOX6.1.30系统版本:ubuntu-20.04.3-desktop-amd64(最小安装模式,中文)OpenCV版本:4.5.5安装时间:2022.2.11下面是步骤1、进入OpenCV的官方下载地址Releases - OpenCV,下载So_无法定位软件包 libgazebo-dev
文章浏览阅读320次,点赞6次,收藏10次。/是上一个的进化版,相邻的可以一样但是不能都是绿色,注意条件;~~~//仅当笔者个人备忘录使用。
文章浏览阅读6.7k次。由于Install Intel x86 Emulator Accelerator (HAXM installer) (revision: 7.6.5)安装失败,导致我的安卓虚拟机无法启动。解决办法有一下几种:1.开机进入BIOS打开Virtual虚拟化功能,然后进入Androidstudio 的SDK manager里面安装HAXM2.关闭系统中的Hyper-v,进入控制面板的程序和功能,将Hyper-v去选即可。3.如果前面的方法都不行,那么建议你重新下载AndroidStudio最新版进行安装_intel x86 emulator
文章浏览阅读1.1w次,点赞2次,收藏15次。 每一种语言都有一组基本的语法约定,POWERBUILDER也不例外。 (1)断行、续行与多条语句 通常情况下,powerbuilder的一条语句是写到一行上的,该条语句在书写完毕之后,按键转到下一行,开始写下一句的内容。也就是说,在PowerBuilder中,使用键作为一行的结束。在PowerBuilder语句比较长的情况下,为了方便阅读,可以使用续行符号把一条语句写到几_powerbuilder