DBus-Cherrypy合并问题

0 投票
2 回答
755 浏览
提问于 2025-04-15 17:27

我正在使用python-dbus和cherrypy来监控USB设备,并提供一个REST服务来维护插入的USB设备的状态。我已经独立编写并调试了这些服务,它们都能正常工作。

现在,我想把这些服务合并成一个应用程序。我的问题是:我似乎无法让这两个服务(cherrypy和dbus)同时启动。要么一个服务阻塞,要么另一个服务失效,或者没有被初始化。

我尝试把每个服务放在自己的线程里,然后直接调用启动。这导致了一些奇怪的问题。

class RESTThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        cherrypy.config.update({ 'server.socket_host': HVR_Common.DBUS_SERVER_ADDR, 'server.socket_port': HVR_Common.DBUS_SERVER_PORT, })
        cherrypy.quickstart(USBRest())

class DBUSThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)

    def run(self):
        DBusGMainLoop(set_as_default=True)
        loop = gobject.MainLoop()
        DeviceAddedListener()
        print 'Starting DBus'
        loop.run()

print 'DBus Python Started'
if __name__ == '__main__':
    # Start up REST

    print 'Starting REST'
    rs = RESTThread()
    rs.start()

    db = DBUSThread()
    db.start()

    #cherrypy.config.update({ 'server.socket_host': HVR_Common.DBUS_SERVER_ADDR, 'server.socket_port': HVR_Common.DBUS_SERVER_PORT, })
    #cherrypy.quickstart(USBRest())

    while True:
        x = 1

当运行这段代码时,cherrypy的代码没有完全初始化。当插入USB设备时,cherrypy继续初始化(好像线程之间有某种联系),但并没有正常工作(没有提供数据,甚至在端口上也没有建立连接)。我查看了cherrypy的维基页面,但没有找到一种方法可以让cherrypy以这样的方式启动:初始化后返回,这样我就可以初始化DBus的部分,并能顺利完成这个项目。

我最终想问的是:有没有办法让cherrypy启动后不阻塞,继续工作?我希望在这个例子中去掉线程,在主线程中同时初始化cherrypy和dbus。

2 个回答

3

是的;不要使用 cherrypy.quickstart。相反,你可以把它拆开来用:

cherrypy.config.update(conf)
cherrypy.tree.mount(USBREST())
cherrypy.engine.start()

quickstart 做了上面的事情,但最后会调用 engine.block()。如果你的程序有其他的主循环,而不是 CherryPy 的主循环,就可以省略对 engine.block 的调用,这样就没问题了。不过,当你自己主循环结束时,还是要调用 cherrypy.engine.stop():

loop = gobject.MainLoop()
try:
    loop.run()
finally:
    cherrypy.engine.stop()

还有一些其他的小细节,比如 CherryPy 是否应该处理 Ctrl-C 和其他信号,或者它是否应该自动重载。这些行为由你来决定,开启或关闭都比较简单。可以查看 cherrypy.quickstart() 的源代码,里面有一些相关的内容。

3

我搞明白了。这看起来是glib里有很多线程竞争的问题。如果你在应用程序里使用了DBusGMainLoop,那么你就不能再创建其他线程了。当你尝试启动新线程时,它会立刻被阻塞。无论你怎么调整,都无法让新线程正常运行。

我找到一个网站,上面提到了一些不太常见的内容,提到需要在初始化新线程之前调用dbus.mainloop.glib.threads_init()。但是当我尝试这样做时,又出现了一个新问题。系统抛出了一个异常,提示必须先调用g_thread_init(),才能调用dbus.mainloop.glib.threads_init()。经过进一步搜索,我发现了另一个不太常见的内容,提到gobject.threads_init()。这似乎是对的,所以经过多次实验,我找到了正确的调用顺序。

这就是解决方案。

dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
gobject.threads_init()
dbus.mainloop.glib.threads_init()    
DBUSMAINLOOP = gobject.MainLoop()

print 'Creating DBus Thread'
DBUSLOOPTHREAD = threading.Thread(name='glib_mainloop', target=DBUSMAINLOOP.run)
DBUSLOOPTHREAD.start()

print 'Starting REST'
cherrypy.config.update({ 'server.socket_host': Common.DBUS_SERVER_ADDR, 'server.socket_port': Common.DBUS_SERVER_PORT, })
cherrypy.quickstart(USBRest())

真是个噩梦。现在要想办法改善它。

撰写回答