加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

如何处理ZeroMQ Ruby中的线程问题?

发布时间:2020-12-16 20:57:05 所属栏目:百科 来源:网络整理
导读:在阅读 ZeroMQ FAQ关于线程安全时偶然发现. My multi-threaded program keeps crashing in weird places inside the ZeroMQ library. What am I doing wrong? ZeroMQ sockets are not thread-safe. This is covered in some detail in the Guide. The short
在阅读 ZeroMQ FAQ关于线程安全时偶然发现.

My multi-threaded program keeps crashing in weird places inside the ZeroMQ library. What am I doing wrong?

ZeroMQ sockets are not thread-safe. This is covered in some detail in the Guide.

The short version is that sockets should not be shared between threads. We recommend creating a dedicated socket for each thread.

For those situations where a dedicated socket per thread is infeasible,a socket may be shared if and only if each thread executes a full memory barrier before accessing the socket. Most languages support a Mutex or Spinlock which will execute the full memory barrier on your behalf.

我的多线程程序在ZeroMQ库中的奇怪位置不断崩溃.
我究竟做错了什么?

以下是我的以下代码:

Celluloid::ZMQ.init
module Scp
    module DataStore
    class DataSocket
        include Celluloid::ZMQ 
            def pull_socket(socket)
                @read_socket = Socket::Pull.new.tap do |read_socket|
                    ## IPC socket
                    read_socket.connect(socket)
                end
            end

            def push_socket(socket)
                @write_socket = Socket::Push.new.tap do |write_socket|
                    ## IPC socket
                    write_socket.connect(socket)
                end
            end

            def run
                pull_socket and push_socket and loopify!
            end

            def loopify!
                loop {
                   async.evaluate_response(read_socket.read_multipart)
                }
            end

            def evaluate_response(data)
                return_response(message_id,routing,Parser.parser(data))
            end

            def return_response(message_id,object)
                data = object.to_response
                write_socket.send([message_id,data])
            end
        end
    end
end  

DataSocket.new.run

现在,有几件我不清楚的事情:

1)假设异步生成一个新线程(每次)并且write_socket在所有线程之间共享,而ZeroMQ表示它们的套接字不是线程安全的.我当然看到write_socket运行到线程安全问题.
(顺便说一句,到目前为止,在所有端到端测试中都没有遇到过这个问题.)

问题1:我的理解是否正确?

为了解决这个问题,ZeroMQ要求我们使用Mutex,Semaphore实现这一目标.

这导致问题2

2)上下文切换.

鉴于线程应用程序可以随时切换上下文.
查看ffi-rzmq代码Celluloid :: ZMQ .send()在内部调用send_strings(),内部调用send_multiple()

问题2:上下文切换可以(在任何地方)内部发生(甚至在关键部分)(这里)[https://github.com/chuckremes/ffi-rzmq/blob/master/lib/ffi-rzmq/socket.rb#L510]

这也可能导致数据排序问题.

我的以下观察是否正确?

注意:

Operating system ( MacOS,Linux and CentOS )  
Ruby - MRI 2.2.2/2.3.0

解决方法

没有人应该把应用程序的稳健性放在薄薄的冰上

原谅这个故事是一个相当长的阅读,但作者的终身经验表明,为什么比任何少数SLOC(可能是可疑的或神秘的或根本原因无知的)试图通过实验找到

初步说明

虽然ZeroMQ几十年来一直被推广为零共享(零阻塞,(几乎) – 零延迟和更多设计准则.阅读有关优缺点的最佳地点是Pieter HINTJENS的书籍,而不仅仅是这个神奇的“Code Connected,第1卷”,以及真正的社交领域的高级设计和工程理念,最近的API文档引入并宣传了一些恕我直言的功能,并放松了与这些分布式的角石原则 – 计算,零分享这么大声,不那么尖锐的哨声.这就是说,我仍然是一个零分享的家伙,所以请从这个角度看这篇文章的其余部分.

答案1:不,先生. – 或者更好 – 是和否,先生.

ZeroMQ不要求人们使用Mutex / Semaphore障碍.这与ZeroMQ设计格言相矛盾.

是的,最近的API更改开始提到(在某些附加条件下)可能会开始使用共享套接字……有(许多)其他措施……所以暗示相反.如果一个人“想要”,那个人还会采取所有额外的步骤和措施(并支付所有最初隐藏的设计和实施成本,以“允许”共享玩具(希望)在与其他人的主要(非必要)战斗中生存无法控制的分布式系统环境 – 因此突然也存在失败的风险(由于许多明智的原因而不是初始ZeroMQ零共享福音传播的情况) – 因此,用户决定走哪条路.公平的.)

声音&强大的设计IMHO仍然有更好的开发按照初始的ZeroMQ API&传福音,零分享是一个原则.

答案2:设计总是存在关于ZeroMQ数据流排序的主要不确定性,ZeroMQ设计准则之一使设计者不要依赖于对消息排序的不支持的假设以及许多其他假设(例外情况适用).可以肯定的是,分配到ZeroMQ基础架构的任何消息都是作为完整消息传递的,或者根本不传递.因此,人们可以确定这样一个事实,即交付时不会出现零碎的残骸.有关详细信息,请阅读以下内容.

ThreadId没有任何证据(除非使用了inproc传输类)

鉴于ZeroMQ数据泵浦引擎的内部设计,实例化了一个
zmq.Context(number_of_IO_threads)决定生成多少个线程来处理未来的数据流.这可能是{0,1:default,2,..}的任何地方,几乎耗尽了内核修复的最大线程数. 0的值给出了一个合理的选择,在不浪费资源的情况下,其中inproc:// transport-class实际上是一个直接内存区域映射处理数据流(实际上从来没有流动直接固定到登陆 – 接收套接字抽象的填充:o))并且此类作业不需要任何线程.
接下来,< aSocket> .setsockopt(zmq.AFFINITY,< anIoThreadEnumID#>)允许微调与数据相关的IO-“液压”,以便优先处理,负载平衡,性能调整线程加载到zmq.Context()的枚举池中 – 实例的IO线程,并从上面列出的设计中获得更好和最好的设置&数据流操作方面.

基石元素是Context()s’实例,而不是Socket()实例

一旦Context()的实例被实例化和配置(参见上面的原因和方法),它(几乎)可以自由共享(如果设计无法抵抗共享或需要避免设置完全成熟的分布式计算基础设施).

换句话说,大脑总是在zmq.Context()的实例中 – 所有与套接字相关的dFSA引擎都在那里设置/配置/操作(是的,即使语法是< aSocket> .setsockopt(. ..)这种效果是在大脑内部实现的 – 在相应的zmq.Context中 – 而不是在某些线路中从A到B.

最好永远不要分享< aSocket> (即使API-4.2.2承诺你也可以)

到目前为止,人们可能已经看到了很多代码片段,其中ZeroMQ Context和它的套接字被实例化并快速处理掉,只连续服务几个SLOC-s,但是 – 这并不意味着,这样的实践是明智的或通过任何其他需要进行调整,而不是一个非常学术的例子(由于图书出版商的政策,仅需要在尽可能少的SLOC中打印).

即使在这种情况下,也应该存在关于zmq.Context基础设施设置/拆除的巨大成本的公平警告,因此为了避免任何泛化,这种代码的任何复制/粘贴副本的使用就越少,仅仅是用于短时间的出于说明的目的.

想象一下为任何单个Context实例发生的现实设置 – 准备好各自的dFSA引擎池,维护它们各自的配置设置以及所有与套接字端点池相关的传输类特定硬件外部O. / S-services处理程序,循环事件扫描程序,缓冲区内存池分配它们的动态分配器等等.这都需要时间和O / S资源,因此要明智地处理这些(自然)成本并小心处理调整后的开销,如果性能不受影响.

如果仍然有疑问为什么要提这个,想象一下,如果有人坚持在发送数据包之后立即拆除所有LAN电缆,并且需要等到需要发送下一个数据包之前安装新的电缆出现.希望这个“合理实例化”视图现在可以更好地被识别,并且可以分享(如果有的话)zmq.Context() – 实例,而不需要再尝试共享ZeroMQ套接字实例(即使新成为(几乎)线程安全的本身).

如果将ZeroMQ理念视为高性能分布式计算基础架构的高级设计传播,那么它就是强大的.仅调整一个(次要)方面通常不会调整所有的工作和成本,就像如何设计安全和高性能系统的全局观点一样,结果不会更好地移动一点
(甚至绝对可共享的无风险(如果有可能)套接字实例也不会改变这一点,而声音设计,清洁代码和合理可行的测试能力和调试的所有好处将得到如果只是这一个细节被改变了 – 所以,而是将现有大脑中的另一根电线拉到这样一个新线程,或者用它自己的大脑装备新线程,这将在本地处理它的资源并允许它连接自己的电线回到所有其他大脑 – 必要时与分布式系统进行通信 – .

如果还有疑问的话,试着想象一下,如果你们在比赛期间只分享一个单曲棍球棒,那么你的全国奥运会曲棍球队会发生什么.或者你想怎么样,如果你家乡的所有邻居都会分享同一个电话号码来接听所有来电(是的,同时拨打所有电话和手机,共享同一号码).这有多好?

语言绑定无需反映所有可用的API功能

在这里,人们可以提出并且在某些情况下是正确的,并非所有ZeroMQ语言绑定或所有流行的框架包装器都将所有API细节都暴露给用户进行应用程序级编程(这篇文章的作者已经挣扎了很长时间有这样的遗留冲突,这仍然是无法解决的,并且不得不为了找到任何可行的方法来解决这个问题而抓挠头脑 – 所以它(几乎)总是可行的

结语:

值得注意的是,ZeroMQ API 4.2.2的最新版本开始悄悄地传播福音传播原则.

尽管如此,值得记住忧郁的纪念品

(重点补充,不是大写)

Thread safety

?MQ has both thread safe socket type and not thread safe socket types. Applications MUST NOT use a not thread safe socket from multiple threads except after migrating a socket from one thread to another with a “full fence” memory barrier.

Following are the thread safe sockets: * ZMQ_CLIENT * ZMQ_SERVER * ZMQ_DISH * ZMQ_RADIO * ZMQ_SCATTER * ZMQ_GATHER

虽然这篇文章可能听起来很有希望,但在设计高性能分布式计算系统时,人们可以做的最糟糕的事情就是设置服务障碍.

人们希望看到的最后一件事就是阻止一个人自己的代码,因为这样的代理进入了一个主要无法控制的阻塞状态,在这种状态下,没有人可以跟踪它(内部代理本身也不是外部的任何人),如果远程代理从不提供正常预期的事件(在分布式系统中可能由于很多原因或在很多情况下不在一个人的控制范围内).

构建一个易于自我挂起的系统(具有支持(但天真使用)语法可能性的广泛微笑)确实没什么好开心的,不是一个严肃的设计工作.

在这里,人们也不会感到惊讶,许多额外的(最初不可见的)限制适用于使用共享的新动作 – {hockey-stick |电话} API:

ZMQ_CLIENT sockets are threadsafe. They do not accept the ZMQ_SNDMORE option on sends not ZMQ_RCVMORE on receives. This limits them to single part data. The intention is to extend the API to allow scatter/gather of multi-part data.

C / A

Celluloid :: ZMQ在其支持的套接字类型的部分中没有报告任何这些新的API-(共享几乎宽容的插件)套接字类型因此没有预期的先验和Celluloid :: ZMQ主活动似乎没有好消息在2015年已经逐渐消失,因此从这个角落来看,预期应该是现实的.

这就是说,通知背后可能会发现一个有趣的观点:

before you go building your own distributed Celluloid systems with Celluloid::ZMQ,be sure to give 07000 a look and decide if it fits your purposes.

最后但同样重要的是,在另一个事件循环中组合事件循环系统是一项痛苦的工作.试图将嵌入式硬实时系统集成到另一个硬实时系统中甚至可以在数学上证明自己是不可能的.

类似地,使用另一个基于代理的组件构建多代理系统会带来其他类型的冲突和竞争条件,如果遇到相同的资源,则可以利用这些资源(无论是有意识的还是“只是”某些功能性副作用)(多个)基于代理的框架.

不可挽回的相互死锁只是这些碰撞中的一种,它引发了一些不知不觉的设计尝试中最初未见过的麻烦.单一代理系统设计之外的第一步使得人们失去了更多的保证,这些保证在进入多智能体(分布式)之前就没有被注意到,因此开放思想并准备好学习许多“新”概念和注意力关于需要仔细观察并努力避免的许多新问题是非常重要的先决条件,以免(无意识地)引入模式,现在实际上是分布式系统(多智能体)域中的反模式.

至少你被警告:o)

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读