是否存在演员模型不适合的并发设计?

时间:2013-06-24 19:18:52

标签: multithreading design-patterns concurrency

我注意到我遇到的所有设计都可以使用actor模式进行多线程 - 将每个工作模块分成不同的actor并使用消息队列(对我来说是一个.NET ConcurrentQueue)来传递消息。还有哪些好的多线程模型?

5 个答案:

答案 0 :(得分:12)

我认为,传递顺序进程是一种比参与者模型更好的并发模型。它解决了actor模型(和其他模型)的许多问题,例如死锁,活锁,饥饿。看看this,更实际有用,this

主要区别如下。在actor模型中,异步发送消息。但是在CSP中,消息是同步发送的;发送方无法发送,直到接收方准备接收为止。

这一个简单的限制使世界变得不同。如果你的设计有不正确的死锁,那么在演员模型中它可能会或可能不会发生(而且通常只有在向老板演示时才会发生......)。但是在CSP中,总是会发生死锁,让您毫无疑问地认为您的设计不正确。好的,所以你仍然需要解决它但是没关系;修复你知道的问题比试图彻底测试没有问题(你在演员模型中唯一的选择)要容易得多。

CSP的严格同步方法似乎会导致响应时间出现问题;例如,有人担心GUI线程无法继续运行,因为它无法向繁忙的工作线程发送消息,而该线程没有达到“读取”的程度。您需要做的是确保工作负载分布在足够的线程中,以便他们都可以在可接受的时间段内返回等待新消息。 CSP不会让你逃脱它。演员模型确实不会被欺骗;你只是在构建未来的问题。

在.NET中,ConcurrentQueue不是CSP的正确原语,除非您在顶层分层同步机制。我也在TCP套接字上添加了严格的同步。事实上,我通常最终会编写某种抽象套接字和管道的库,这样就无法确定“进程”(因为它们在CSP用语中已知)是该机器上的线程还是整个其他进程在网络连接结束时的另一台机器上。很好 - scalabilty从一开始就内置。

我已经用CSP方式做了23年了,我不会做任何其他方式。用这种方式构建了一些拥有数千个线程的大型系统。

== EDIT ==

似乎这个答案仍然吸引了一些注意力,所以我想我会加入它。对于Windows开发人员,任务并行库有DataFlow命名空间。它必须单独下载。微软如此嘲笑它:“这种数据流模型通过为粗粒度数据流和流水线操作提供进程内消息传递来促进基于actor的编程。”优秀!它使用BufferBlocks之类的类作为通信渠道。重要的是BufferBlock具有BoundedCapacity属性,默认为Unbounded,适合Actor模型。将此值设置为1,您现在已将其转换为CSP样式的通信通道。

答案 1 :(得分:5)

要添加到我的上一代,除了CSP之外还有其他各种多线程模型。此Wikipedia page列出了其他几个广告,例如CCSACPLOTOS。阅读这些文章暗示了学术界漫游的深洞和黑暗的洞穴,等待扑向流浪软件开发者。

问题在于,学术上的默默无闻通常意味着在实际可用的水平上完全缺乏工具和库。将经过验证的经过验证的学术研究转化为一套图书馆和工具需要付出很多努力。对于更广泛的软件社区来说,采用理论论文并将其转化为实际现实并没有什么真正的动力。

我喜欢CSP,因为基于select()或pselect()实现自己的CSP库实际上很简单。我现在已经多次这样做了(我必须学习代码重用),还有肯特大学的优秀人才为那些喜欢Java的人安排了JCSP。我不建议在奥卡姆开发(尽管它仍然可能);支持和可维护性将成为未来的问题。 CSP可能是最容易进入的人,并且鉴于其良好的特性,它非常值得。

答案 2 :(得分:5)

@JeremyFriesner

未来问题

为了扩展我对“未来问题”的意思,我指的是在异步系统中,消息的发送者不知道接收者是否真正跟上需求。发件人不知道,因为它知道的是某些消息缓冲区已接受该消息。下面的传输(例如tcp)随后在接收者愿意接受它的同时继续推送消息。

因此,在压力下,系统可能无法按要求执行,因为消息传输将不可避免地具有吸收接收方尚未接受的消息的有限容量。发件人只有在问题已经开始发展之后才发现,这时候对它做任何事情都可能为时已晚。

测试当然可以揭示这个问题,但你必须要小心,测试确实已经耗尽了传输的吸收消息的能力。快速全速爆炸可能是骗人的。

当然,一个同步系统会产生开销(“你准备好了吗?”,“不,还没有”,“现在?”,“是的!”,“这里你就是那么”)这不是发生在异步系统中。因此,平均而言,异步系统将更高效,实际上可能具有更高的吞吐量等。这就是为什么世界上大多数系统实际上是异步的,而且系统并不总能达到原始系统的全部容量的原因。网络带宽/处理时间可能暗示。在我看来,当接近满容量时,异步系统往往不会优雅地限制。令牌总线(nb 令牌环)是同步网络的一个很好的例子,具有完全可靠和确定的吞吐量,但只比以太网和令牌环慢一点......

在我的问题中,我总是幸运地拥有过多的带宽,但出于成功的原因,我选择了同步路线;我并没有真正失去带宽,但我失去了很多风险,这很好。

从同步转换为异步

也许,但它可能没什么价值。在同步系统中,如果您已成功平衡线程之间的分工,则它仅按要求工作。也就是说,有足够的线程执行慢位,以便不会阻止快速位。弄错了,系统肯定不够快。

但是,在这样做之后,你就拥有了一个系统,每个组件都能够毫无延迟地向前发送消息,因为它发送的所有内容都已准备好并等待(因为你的技能和判断能够平衡工作负载)。因此,如果您确实转换为异步消息传输,那么您所做的只是在传输这些消息时节省很少的时间。您没有进行更改,这会导致工作负载得到更快的处理。但是,如果节省带宽是目标,那么也许是值得的。

当然,进行这种平衡可能是一件困难的事情,处理硬盘访问时间,网络等变化可能很难克服。我经常不得不实施“下一个可用的”工作负载共享方案。但实际上我和你一起玩的信号处理系统基本上处理的是像OpenVPX的RapidIO这样的非常可靠的传输,你只需要处理数据(不处理数据库,磁盘等),以及数据速率非常高(1GByte / sec这些天是完全可行的,事实上我处理的数据率高达13年前;这是哈佛的工作)。严格同步意味着您要么明确地跟上数据速率,要么绝对不是。对于异步,它更像是一个......

适合所有人的实时操作系统!

拥有实时操作系统也是一个必不可少的组件,而现在它似乎是Linux的PREEMPT_RT补丁集,可以为很多人提供服务。 Redhat做了一个预包装旋转(RedHat MRG),但是对于免费的科学Linux来自CERN的好人们是好的和免费的!我强烈怀疑如果使用PREEMPT_RT,很多系统在容量限制附近会更顺畅地工作 - 它可以很好地平滑事物。

答案 3 :(得分:4)

并发是一个引人入胜的话题,有许多实现方法,基本问题是 - “我如何协调并行计算?”

一些并发模型是:

期货

期货也称为承诺任务是充当异步计算结果的代理的对象。当计算实际需要该值时,线程会冻结,直到计算完成,从而实现同步。

期货是 .NET ES6 的首选并发模型。

软件事务内存

软件事务内存(STM)通过将操作分组到事务中来同步对共享内存的访问(很像锁)。任何单个事务只能看到共享内存的单个视图,并且是原子的。这在概念上类似于有多少数据库处理并发。

STM是 Clojure Haskell 的首选并发模型。

演员模型

消息传递的Actor模型焦点。一个演员收到一条消息,可以决定发送一条消息作为回应,产生其他演员,进行局部改变等。这可能是这些讨论中最紧密耦合的模型,因为Actors只交换消息而没有别的。

Actor模型是 Erlang Rust 的首选并发模型。


请注意,与上面提到的语言不同,大多数语言都没有大炮或首选并发模型,甚至那些对一个模型表现出强烈偏好的语言通常也会将其他语言实现为库。

我个人认为,Futures在使用和推理简单方面远远超过STM和Actors,但这些模型都没有本身的“错误”,我认为两者都没有任何缺点。你可以使用你喜欢的任何一个没有后果。

答案 4 :(得分:1)

最常用的并行处理模型是Petri Nets。它将计算表示为纯数据依赖图,它表示最大并行度。所有其他模型都源自它。

数据流计算模型http://www.cs.colostate.edu/cameron/dataflow.htmlhttp://en.wikipedia.org/wiki/Dataflow_programming几乎同样强大。它限制Petri Net位置只有一个输出弧。在实践中,这很有用,因为具有多个输出弧的地方难以实现,导致不确定性,并且很少需要。

Actor模型是一种数据流模型,其中节点可能只有2个输入边 - 一个用于输入消息,一个用于actor的状态。如果您想编写具有副作用和多个参数的函数,这是一个严重的限制。