具有直观的并发编程抽象的现代编程语言


40

我对学习并发编程感兴趣,重点是应用程序/用户级别(而不是系统编程)。我正在寻找一种现代的高级编程语言,该语言提供用于编写​​并发应用程序的直观抽象。我想专注于提高生产率并隐藏并发编程复杂性的语言。

举个例子,我不认为用C,C ++或Java编写多线程代码是一个好选择,因为恕我直言,我的工作效率降低了,而且它们的编程模型也不直观。另一方面,提高生产率并提供更直观抽象的语言(例如Python和多处理模块,Erlang,Clojure,Scala等)将是不错的选择。

根据您的经验,您会推荐什么?为什么?

编辑:谢谢大家的有趣的回答。很难尝试得出结论,因为有很多不错的候选人:Erlang,Clojure,Scala,Groovy以及Haskell。我以最有说服力的论据对答案进行了投票,但是在决定选择哪个候选人之前,我将尝试所有好的候选人:)


21
To give an example, I don't consider a good option writing multithreaded code in C, C++, or Java。为什么?On the other hand, Python and the multiprocessing module, Erlang, Clojure, Scala, etc. are some of my options.同样,为什么呢?扩展您的问题以更好地定义您实际要寻找的内容。
yannis,2011年

2
那么,您是否想学习所有陷阱的并行编程,还是想隐藏它的一些复杂性并关注生产率?
2011年

@MaR关注生产力并隐藏复杂性:)
sakisk 2011年

只是请注意,在其中某些语言中避免了许多重要的概念(有人说可以解决),因此C确实是学习并发的最佳语言。(或者至少对我来说是如此;我对所列的所有语言都不了解)。生产率的提高通常与全面学习相冲突。
2011年

1
@DeadMG生产力下降是他们的问题。我不想只关注语言的语法而不是问题。我绝对不想最终陷入僵局。作为一个简单的示例,我想使用诸如begin transaction end transaction和这样的简单语句,并且内部的所有内容都应无死锁,并且整体上成功或失败。
sakisk 2011年

Answers:


33

您几乎可以肯定应该看一下Clojure-在我看来,它是用于多核编程的最佳现代语言,并且效率很高。

关键属性:

  • 这是一种功能性语言,它对并发性和使用高级抽象进行开发的能力都有好处。它具有完全不变的持久性数据结构和惰性序列,对使用Haskell这样的功能语言有经验的人都会熟悉。
  • 它具有一个非常新颖的软件事务存储系统,用于同时无锁访问可变状态。使代码并发安全通常与将其包装在(dosync ....)块中一样简单。
  • 这是一个Lisp,它对于基于宏的元编程和代码生成非常强大。这可以带来显着的生产力优势(Paul Graham的文章-“超越平均水平”)
  • 它是一种JVM语言 -因此,您不仅可以访问Java生态系统中大量的库和工具,而且还可以从使JVM成为并发服务器端应用程序有效平台的大量工程工作中受益。出于实际目的,与没有这种基础的语言相比,它具有巨大的优势。
  • 它是动态的 -产生非常简洁的代码并提高了生产力。但是请注意,可以根据需要使用可选的静态类型提示来提高性能。
  • 该语言是围绕抽象设计的,这在某种程度上很难解释,但是最终的结果是,您获得了一组相对正交的功能,可以将它们组合起来以解决问题。一个例子就是序列抽象,它使您能够编写处理每种“序列”对象类型的代码(包括列表,字符串,Java数组,无限延迟序列,从文件中读取行等的所有内容)。
  • 有一个很棒的社区 -乐于助人,有见识但最重要的是非常务实-Clojure的重点通常是“把事情做好”。

一些具有并发倾向的迷你代码示例:

;; define and launch a future to execute do-something in another thread
(def a (future (do-something)))

;; wait for the future to finish and print its return value
(println @a)

;; call two functions protected in a single STM transaction
(dosync
  (function-one)
  (function-two))

特别值得一提的是观看以下一个或多个视频:


21
强类型语言中的静态类型声明的目的不是“在需要的地方提高性能”,而且我有点讨厌Lisp倡导者大肆宣传那个老稻草人。类型声明有两个目的:提供一定的编译时保证正确性,并使代码更易于阅读,特别是对于原始作者以外的人。静态类型提供的本质上更好的性能只是一个好处。
梅森惠勒

8
最近,我不得不与另一个开发人员的JavaScript代码一起工作,这是该过程中最痛苦的部分:在函数参数上没有类型的情况下,我必须遍历整个代码库以弄清楚它们应该做什么。 be,以及根据他们来自哪里被调用的方法。如果JavaScript除了通用语法外还保留了C的类型系统,那将是没有问题的。
梅森惠勒

1
@MasonWheeler:恕我直言,如果您不知道如何在没有类型注释的情况下调用函数,则说明文档存在问题(或缺少文档)。即使在鸭子式语言中,所有内容通常也必须满足某些结构类型约束(例如,必须支持算术运算,必须可迭代,必须可索引等)。静态类型只会有助于最小,因为他们不会给出有关的功能是什么太多的暗示
dsimcha 2011年

2
@Mason我从未说过静态类型声明没有其他优点。实际上,出于您陈述的原因,我喜欢静态类型声明。但是,我也喜欢动态键入带来的生产力提高。这是一个权衡。如果您有一个好的测试套件,我通常会发现这在确保正确性和提供示例代码以帮助新手了解正确用法方面都减轻了动态类型的许多弊端。YMMV。
mikera 2011年

1
@dsimcha-围绕抽象设计的替代方法是围绕具体实现进行设计。例如,大多数旧的Lisp函数仅对存储在con单元格中的链接列表起作用。您需要针对不同数据结构的不同功能。在Clojure中,核心库函数可对任何顺序的东西起作用(如答案所示)。
mikera 2011年

27

您可以尝试D。它提供了三种模型。我建议第一或第二。

  1. std.concurrency。如果您将此模块用于所有并发需求,那么语言和标准库的组合将强制线程之间实现隔离。线程主要通过消息传递进行通信,并且对共享内存的支持有限,从而有利于“安全第一”并禁止低级数据争用。不幸的是,std.concurrency的文档需要改进,但是在Andrei Alexandrescu的书“ The D Programming Language”免费一章中记录了该模型

  2. 标准并行。该模块是专为多核并行而不是一般情况下的并发设计的。(虽然并发和并行性不是同一件事,尽管并发对于实现并行性是必需的。)由于并行性的全部是性能,因此std.parallelism不能提供任何隔离保证,因为它们会使编写高效的并行代码变得困难。但是,它确实抽象出了许多容易出错的底层细节,因此,如果您要并行化手动验证的工作负载是相互独立的,则很难搞砸。

  3. core.thread是操作系统特定的线程API的低级包装。std.concurrency和std.parallelism都在后台使用,但我只建议在编写自己的并发库或发现一些在std.parallelism或std中都无法很好完成的荒谬案例时使用.concurrency。没有人可以在日常工作中使用这种低级别的内容。


您应该已经提到了不变性/纯度,默认情况下的线程本地存储以及按顺序施加突变的共享。这些都是C / C ++中缺少的语言支持,无法编写一致的代码。
deadalnix

@deadalnix:对我来说,其中大多数是std.concurrency模型的详细信息(如何执行隔离)。我想保持这篇文章的简洁。
dsimcha 2011年

好吧,实际上没有。并发性需要库和语言的支持。
deadalnix

@deadalnix:是的,但是它们在很大程度上是为了支持std.concurrency而设置的。
dsimcha

23

Erlang绝对是一个不错的选择,但更实用的方法可能是Google的新语言Go

它与其他通用语言相距不远,因此如果您已经知道其他“简单”语言,通常就很容易获得。就编程的“舒适性”而言,许多人将其与Python甚至Lua进行了比较。


@faif在询问应用程序/用户级别,而不是系统并发编程。Erlang如何适应呢?
凯龙

@Raynos:取决于社区。
Donal Fellows,

@DonalFellows您的权利,我认为我的发言太狭窄了
Raynos

1
@Chiron:Erlang是一种编程语言,用于创建应用程序。通常,多处理应用程序。我不知道它在哪里适合作为“系统并发编程”,我还没有听说过任何用Erlang编写的操作系统。
哈维尔

1
在Go教程中快速浏览之后,我想说恕我直言,一种具有C语言语法的语言(使用(有限)指针)绝对不是一种能够提高生产率的现代语言。
sakisk 2011年

23

看一下Microsoft的.net并行编程。这是非常直观的。

许多个人计算机和工作站具有两个或四个核心(即CPU),它们可以同时执行多个线程。预计不久的将来,计算机将拥有更多的内核。为了利用当今和未来的硬件,您可以并行化代码以在多个处理器之间分配工作。过去,并行化要求对线程和锁进行低级操作。Visual Studio 2010和.NET Framework 4通过提供新的运行时,新的类库类型和新的诊断工具,增强了对并行编程的支持。这些功能简化了并行开发,因此您可以自然的习惯用法编写高效,细粒度和可伸缩的并行代码,而不必直接使用线程或线程池。 http://i.msdn.microsoft.com/dynimg/IC292903.png


+1这正是他所要的。但是,当确实发生问题时,如果不了解底层的并发性将很难调试它们。更不用说,将其作为C#初学者可以证明...有趣。
P.Brian.Mackey

@ P.Brian.Mackey-我同意。但是,这种情况并不少见,它不会是一个夸张地比较这对使用奥姆斯当一个不完全理解的关系模型和SQL ...
OtávioDécio

1
特别是PLINQ。尽管它仅对一小部分任务有用,但它非常易于使用。
svick 2011年

21

Erlang和Scala都具有基于actor的并发性,我发现它非常直观且易于学习。

计算机科学中的Actor模型是并行计算的数学模型,该模型将“ actor”视为并行数字计算的通用原语:响应收到的消息,actor可以做出本地决策,创建更多actor,发送更多消息,并确定如何响应收到的下一条消息...它既被用作理论上对计算的理解的框架,也被用作并发系统的几种实际实现的理论基础。


19

我现在正在学习Haskell,阅读本文使我确信Haskell是并发编程的不错选择。因为它是纯粹的功能(类型系统知道某个功能是否对全局状态进行任何输入,输出或读取/修改),所以它可以执行类似于事务处理的功能(如上文中总结得很好的软件事务处理内存)在数据库中-您只需一小笔额外的糖就可以获得一堆好东西,例如原子性。AFAIK,Haskell螺纹也很轻。除了这些功能之外,Haskell纯粹是功能性的事实使即使是简单的任务也可以仅使用一个关键字(par)并行运行。资源


7

Google的GO语言具有一些有趣的并发工具-尝试另一种有趣的事情。请参阅:http : //golang.org/doc/effective_go.html#concurrency,并阅读一些示例。

并发编程是一个很大的话题,这里仅留有一些特定于Go的亮点。

实现对共享变量的正确访问所需的技巧使许多环境中的并行编程变得困难。Go鼓励采用一种不同的方法,在这种方法中,共享的值在通道上传递,实际上,决不由单独的执行线程主动共享。在任何给定时间,只有一个goroutine可以访问该值。根据设计,不会发生数据争用。为了鼓励这种思维方式,我们将其简化为一个口号:

不要通过共享内存进行通信;而是通过通信共享内存。

这种方法可能太过分了。例如,最好通过在整数变量周围放置一个互斥锁来最好地完成引用计数。但是,作为一种高级方法,使用通道来控制访问权限使编写清晰,正确的程序变得更加容易。

考虑该模型的一种方法是考虑一个CPU上运行的典型单线程程序。它不需要同步原语。现在运行另一个这样的实例;它也不需要同步。现在让这两个人交流;如果通信是同步器,则仍然不需要其他同步。例如,Unix管道非常适合此模型。尽管Go的并发方法起源于Hoare的通信顺序过程(CSP),但它也可以看作是Unix管道的类型安全的概括...


6

在下一个版本中,C#使它比该图所示的更加容易。有两个新的关键字Async和Await。

异步用作函数修饰符,并说“此操作在另一个线程上执行其工作。

Async函数中使用了Await,这就是神奇的地方。基本上,Await告诉编译器在单独的线程中按照关键字运行操作,然后等待结果。await调用之后的所有代码将在操作后运行。

另外,该操作将同步到调用线程(因此,如果您正在响应按钮单击而执行了异步操作,则不必手动发回UI线程)。两个小关键字,您将获得很多并发功能。在这里阅读更多


注意,任何体面的OS C#编译器已经支持C#5,异步和等待。
雷诺斯

基本上,Await告诉编译器在单独的线程中按照关键字运行操作,然后等待结果。我想知道这个答案是否正确-异步等待与线程无关。本文解释的很好:没有线程
sventevit

好点子。我想我讲的太简单了。实际上发生的是进行“继续”,该“继续”订阅了完成“等待”中任务的事件。是的,某些I / O操作和thread.sleep()(基本上响应时钟中断)没有线程。但是,没有I / O之类的手动任务又如何呢?可以说我们做了一个值得期待的斐波那契计算器?从技术上讲,该文章是正确的“没有线程”,但实际上从来没有,它一直是我们用来隐藏操作系统为我们所做的细节的概念。
迈克尔·布朗

6

我仍然会推荐C ++。它具有编写必要的并发代码的必要抽象能力。压倒性的可能性是,您做一个工作的库很差,因为做这个工作的好的库是相对较新的,并且确实,很好地使用C ++的知识并不十分普遍。英特尔的TBB才问世几年,而微软的PPL自去年以来才出货。

如果您使用诸如TBB或PPL之类的东西,那么就并发代码而言,编写并发并不是完全无关紧要的,因为并发从来都不是无关紧要的,而是很艰巨的。如果直接使用pthreads或Win32线程,那就难怪您不喜欢它-您实际上是在用此类函数编写汇编程序。但是,使用PPL,您正在谈论的是为您并行化的标准功能算法,用于并发访问的通用数据结构以及类似的东西。


1
查看Boost.Threads或C ++ 0x std::thread(或std::tr1::thread)。实际上,这是一个非常好的抽象,IMO。
greyfade11年

1
@greyfade:他们在PPL或TBB上一无所有。boost::thread只是带有一点RAII的OS包装器。PPL和TBB是真正的并发算法,容器等
DeadMG

6

这里需要Ada的插件,因为它具有并行和并发的所有顶级抽象。否则称为任务分配。另外,正如OP要求直观(主观条件!)一样,我认为以Java为中心的世界可以采用其他方法。


5

如果您可以基于JVM,我建议使用Groovy / Java / GPar,因为它允许参与者,数据流,通信顺序进程(CSP),数据并行性,软件事务内存(STM),代理等。。。有许多高级并发和并行模型,每个模型都有不同的“最佳点”。您不想使用与试图构造的问题的解决方案不一致的模型。只有一种模型的语言和框架会迫使您进行算法黑客攻击。

当然,当我为Groovy和GPars做出贡献时,我可能会被视为有偏见。另一方面,我确实使用CSP和Python,请参见。Python-CSP。

还有一点是,最初的问题是学习,而不是编写生产系统。因此,即使最终的生产工作是使用Just :: Thread Pro或TBB之类的东西在C ++中完成的,而不是基于JVM的,Groovy / Java / GPars组合也是学习的好方法。

(由于宿主网站对垃圾邮件的恐慌,必须删除一些完全合理的URL链接。)


罗素(Russel),如有需要,您可以在聊天室中告诉我您想要链接的内容,我将为您添加这些内容:chat.stackexchange.com/rooms/21/programmers
Dan McGrath



0

在问题和答案中已经多次提到Scala,但是我没有看到对Akka的引用,Akka是可与Scala和Java一起使用的actor实现。


这个答案有什么问题?没有其他答案提到akka,而akka实现了并发编程的高级抽象。
Giorgio 2013年

-1

我认为这取决于您要构建的内容。桌面应用程序还是服务器?我听说(但没有个人经验)node.js非常适合服务器的并发编程(无论是在编写代码还是在性能方面)。如果我想编写一个新的服务器应用程序,我可能会尝试一下。不确定桌面应用程序...我已经用C#编写了很多东西,有些工具很好地掩盖了复杂性,尽管在其他情况下,您必须直面它。


-1

我可能为此感到震惊,但是您读过《 TAOUP》的第7章吗?我正在特别考虑的部分是线程与进程。我发现并发处理的概念使大多数人想到线程,但是我从未见过这样的实例,即比产生子进程更容易,更快速地使用线程。

您正在将构建并发的所有细节都提供给构建您的OS的聪明人。已经有很多通信方法,您不必担心共享资源的锁定。基本上,线程是一种效率hack,属于优化规则。如果没有经过必要性测试,请不要进行优化。

找到一个好的子流程库,例如python的envoy。或者,您可以只用C编写几个单独的程序,然后编写另一个“主”程序来使用fork和pipe来生成子进程并与子进程通信。


3
这与OP明确想要的是相反的……生成进程与手动生成线程一样低级。OP 对并发的高级抽象感兴趣。
康拉德·鲁道夫2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.