(续)signal-slot:python版本的多进程通信的信号与槽机制(编程模式)的库(library) —— 强化学习ppo算法库sample-factory的多进程包装器,实现类似Qt的多进程编程模式(信号与槽机制) —— python3.12版本下成功通过测试
前文:
信号与槽机制,是C++的Qt框架提出的一种并行编程模式,实际上是对原生的并行编程模式进行了一定的封装和包装,是wrap操作。
编程语言原生的并行编程模式,一般是创建多个子进程,每个子进程均拥有一个以上的通信队列,各个进程间的通信通过进程队列进行;如果一个进程想要调用另一个进程中的某个函数,并传递给其参数,那么就在通信队列中传入需要调用对方进程的具体函数名和参数,然后对方进程从队列中取出该函数名和具体参数,并按此执行,这就是不使用Qt信号与槽机制包装之前的原生并行编程通信的模式。但是使用原生的并行通信编程模式其编码比较繁琐,尤其对于一些通信场景比较复杂的情况,往往会因为过多的编码导致逻辑混乱而不利于维护,因此Qt框架提出了信号与槽机制编码模式。
简单的说,也就是在不同的对象中实现了signal函数和slot函数,在不同对象实例化后将信号发送方的signal函数注册到信号接收方的slot函数上,这个操作一般使用connection函数,完成了该函数操作实际上就是完成了signal函数对应的接收方的队列的注册,也就是说在发送方调用signal函数时会自动把自身的函数名和需要调用的对方函数名以及参数指定到接收方的队列上。
在这个项目中对发送方和接收方都需要设置循环事件对象,每个循环事件都相当于一个进程,这个进程会不断的循环执行取队列操作,并根据队列中取出的被调用函数及参数进行调用。需要注意的是,该项目的实现是需要在主进程中实现所有声明的,也就是将signal函数注册到slot函数上是在主进程中实现的,也就是connection函数操作只能在主进程中实现。虽然项目实现signal和slot的注册,并且connection可以随时进行,但是所有的声明和connection操作必须在主进程中执行。
每个带有signal和slot函数的对象都必须绑定到一个EventLoop事件上,不同的EventLoop事件运行在不同的进程上,实际上由于EventLoop事件是一个单独的进程,而所有的signal、slot、connection声明都是在主进程中进行的,因此子进程的EventLoop事件启动后会将主进程中与其绑定的带有signal和slot函数的对象copy到子进程中(spawn方式的子进程生成方式,会将主进程中的一切对象全部copy到子进程中,因为信号与槽机制中对被调用对象的标识是使用身份ID字符串的,因此即使在主进程中进行的注册声明,在copy到子进程中依旧可以毫无影响的同样进行调用,只不过具体执行时是在子进程中)。
也正因为该项目的这种实现方式(子进程启动后copy父进程中的所有对象),那么其实任意一个运行在子进程中的对象在主进程中都是有着一个同样的影子对象的,因此我们在主进程中调用一个绑定在子进程中的对象的signal函数时其实并不是在调用运行在子进程中的对象,而是调用其在主进程中的影子对象,因此可以认为所有在主进程中完成connection的signal函数并且其信号发射操作(emit函数)也是在主进程中进行的,那么气emit操作就是在主进程中进行的;同时由于子进程的copy默认操作,那么在子进程中对绑定到子进程上的对象进行emit操作其实是在子进程中进行的;因此总的来说,signal和slot的connection操作必须在主进程中进行声明和执行,但是emit操作在哪里执行是需要看其被调用的对象绑定到哪个进程上的。
信号与槽机制的核心原理,就是进程和进程间通信时通过队列实现的,一个进程中的对象如果调用另一个进程中的某个对象的函数,其实就是把被调用对象的ID字符串和被调用函数的ID字符串以及参数一并通过队列发送给被调用的进程。
需要注意,signal-slot库通过在主进程中实现对signal和slot的connection的声明和注册,其实就是将slot函数的对象ID字符串及通信队列和函数ID字符串绑定给signal函数,这样在signal函数进行emit操作时其实质就是将信息message连同slot函数的对象ID字符串和函数ID字符串发送给slot函数的对象的通信队列。由于每个slot函数所属对象的队列都是在主进程中声明并绑定在各自的对象内的,而EventLoop都是在所有声明完成后在生成子进程时自动copy父进程的所有对象到子进程中的,因此所有的队列都是在主进程和子进程之间的,并没有子进程之间的队列,由于这种声明注册、绑定、子进程copy方式实现的信号与槽机制,只将每个EventLoop的队列暴露给自身,难以实现子进程之间的通信。
PS. 可以说,这个项目的实现比较复杂,逻辑也比较混乱,虽然可以实现作者的设定目标,但是这个逻辑搞的如此混乱也是难以继续维护的,可以说这个项目就是论文代码的副产品,并不具有延续性。
项目作者给出的待完善的功能:
It is currently impossible to connect a slot to a signal if emitter and receiver objects belong to event loops already running in different processes (although it should be possible to implement this feature). Connect signals to slots during system initialization.
简单的说,就是需要先connection设置好以后才可以start,如果start了以后再connection就不会有限,这个原因就是上面刚才说的这个子进程启动的copy操作,如果子进程启动以后自然是不会再copy父进程中后生成的对象的设置了。
posted on 2024-03-05 12:13 Angry_Panda 阅读(66) 评论(0) 收藏 举报