我现在正在使用Python实现数据订阅者,订阅数据发布者(实际上是ZeroMQ发布者套接字),并在收到任何新消息后收到通知。在我的订户中,消息在收到后被转储到数据处理器。订户完成后也会收到订户的通知。由于数据处理器是用C ++编写的,因此我必须使用简单的C ++模块扩展Python代码。
下面是我的数据订阅者的简化可运行代码示例。代码main.py
,其中模块proc代表处理器,订阅localhost:10000
上的ZeroMQ套接字,设置回调,并通过调用proc.onMsg
将收到的消息发送到处理器。
#!/bin/python
# main.py
import gevent
import logging
import zmq.green as zmq
import pub
import proc
logging.basicConfig( format='[%(levelname)s] %(message)s', level=logging.DEBUG )
SUB_ADDR = 'tcp://localhost:10000'
def setupMqAndReceive():
'''Setup the message queue and receive messages.
'''
ctx = zmq.Context()
sock = ctx.socket( zmq.SUB )
# add topics
sock.setsockopt_string( zmq.SUBSCRIBE, 'Hello' )
sock.connect( SUB_ADDR )
while True:
msg = sock.recv().decode( 'utf-8' )
proc.onMsg( msg )
def callback( a, b ):
print( '[callback]', a, b )
def main():
'''Entrance of the module.
'''
pub.start()
proc.setCallback( callback )
'''A simple on-liner
gevent.spawn( setupMqAndReceive ).join()
works. However, the received messages will not be
processed by the processor.
'''
gevent.spawn( setupMqAndReceive )
proc.start()
简化了模块proc
,导出了三个函数:
setCallback
设置了回调函数,以便在处理邮件时,我的订阅者可以收到通知; onMsg
由订阅者调用; start
设置一个新的工作线程来处理来自订阅者的消息,并使主线程加入以等待工作线程退出。可以在github https://github.com/more-more-tea/python_gil找到完整版源代码。然而,它并没有像我的期望那样运行。添加处理器线程后,订户无法从gevent循环中的发布者接收数据。如果我只是丢弃数据处理器模块,则订户gevent循环可以接收来自发布者的消息。
代码有什么问题吗?我怀疑GIL会干扰消息处理器中pthread的并发性,或者gevent循环被饿死了。任何关于该问题或如何调试它的提示都将受到高度赞赏!
答案 0 :(得分:8)
Global Interpreter Lock本身不会阻止调度线程。 Python C API不会随处运行到pthread库中。这既好又坏。
这很好,因为你可以在C或C ++扩展中一次做多个事情。
这很糟糕,因为你可能会意外违反GIL规则。
GIL的规则(大致)如下:
Py_INCREF()
和Py_DECREF()
等简单内容。pthread_join()
或select()
时,它不会自动释放,这意味着您会阻止整个解释器。指定这些规则的正式版本here。密切关注"非Python创建的线程"部分;这正是你想要做的事情。
阅读您的代码,看起来您未能在procThread()
函数中获取GIL,并且在调用pthread_join()
之前也无法释放它。可能还有其他问题,但对我来说这些问题最为明显。
答案 1 :(得分:1)
我有解决问题的方法和我对Python线程的理解和pthread原生的。
Python线程虽然受GIL保护,但实际上是系统线程。唯一让它们与众不同的是,在运行时,Python线程受GIL保护。由threading.Thread
生成的线程是Python线程,并且在这些线程中运行的所有代码都由GIL自动保护。如果本机线程与Python线程共存并且Python线程将要运行阻塞语句,例如,Python线程中的GIL必须与Py_BEGIN_ALLOW_THREADS
和Py_END_ALLOW_THREADS
一起发布。 I / O,Thread.join,sleep等等。
虽然其他线程在Python世界之外产生,例如通过pthread库,应该在执行Python代码时使用Python C API PyGILState_Ensure
和PyGILState_Release
显式获取GIL(对于纯C / C ++代码,不需要根据我的经验获取Python GIL)在Kevin的回答中。
更新的代码可以在GitHub找到。
如果有任何误解,请给我评论。谢谢大家!