0

我正在使用 python 将硬件 USB 嗅探器设备与供应商提供的 python API 连接,并且我试图在无限循环中的单独线程中从设备中读取(usb 数据包)(工作正常)。问题是我的主循环似乎再也没有被安排(我的读取循环得到了所有的关注)。

代码看起来很像这样:

from threading import Thread
import time
usb_device = 0

def usb_dump(usb_device):
    while True:
        #time.sleep(0.001)
        packet = ReadUSBDevice(usb_device)
        print "packet pid: %s" % packet.pid

class DumpThread(Thread):
    def run(self):
        usb_dump()

usb_device = OpenUSBDevice()
t = DumpThread()
t.start()
print "Sleep 1"
time.sleep(1)
print "End"
CloseUSBDevice(usb_device)
sys.exit(0)

(我可以粘贴实际代码,但由于您需要硬件设备,我认为它不会有太大帮助)。

我希望这段代码在主线程终止整个程序之前开始转储 USB 数据包大约一秒钟。但是,我看到的只是“睡眠 1”,然后该usb_dump()过程将永远运行。如果我在程序的内部循环中取消注释“time.sleep(0.001)”语句,usb_dump()事情就会以我期望的方式开始工作,但是python代码变得无法跟上所有传入的数据包:-(

供应商告诉我这是一个 python 调度程序问题,而不是他们的 api 的错,因此对我没有帮助:

«但是,在 Python 中使用线程时,您似乎遇到了一些细微差别。通过将 time.sleep 放在 DumpThread 线程中,您明确地向 Python 线程系统发出信号以放弃控制。否则,由 Python 解释器决定何时切换线程,它通常在执行了一定数量的字节码指令后才会这样做。»

有人可以确认python是这里的问题吗?是否有另一种方法可以使 DumpThread 释放控制?还有其他想法吗?

4

3 回答 3

3

如果您的供应商是纯 Python代码,您的供应商将是正确的;但是,C 扩展可能会释放GIL,因此允许实际的多线程。

特别是 time.sleep确实释放了 GIL(您可以直接从源代码中检查它,在这里- 查看floatsleep实现);所以你的代码应该没有任何问题。作为进一步的证明,我还做了一个简单的测试,只是删除了对 USB 的调用,它实际上按预期工作:

from threading import Thread
import time
import sys

usb_device = 0

def usb_dump():
    for i in range(100):
        time.sleep(0.001)
        print "dumping usb"

class DumpThread(Thread):
    def run(self):
        usb_dump()

t = DumpThread()
t.start()
print "Sleep 1"
time.sleep(1)
print "End"
sys.exit(0)

最后,关于您发布的代码的几点说明:

  • usb_device 没有被传递给线程。您需要将它作为参数传递或(啊!)告诉线程从全局命名空间中获取它。
  • 与其强制 sys.exit(),不如只发信号通知线程停止,然后关闭 USB 设备。我怀疑您的代码可能会出现一些多线程问题,就像现在一样。
  • 如果您只需要定期轮询,threading.Timer 类可能是您更好的解决方案。

[Update] About the latest point: as told in the comment, I think a Timer would better fit the semantic of your function (a periodic poll) and would automatically avoid issues with the GIL not being released by the vendor code.

于 2009-07-30T09:35:48.313 回答
2

我假设您编写了一个 Python C 模块,该模块公开了 ReadUSBDevice 函数,并且它打算在收到 USB 数据包之前阻塞,然后返回它。

本机 ReadUSBDevice 实现需要在等待 USB 数据包时释放 Python GIL,然后在收到数据包时重新获取它。这允许其他 Python 线程在您执行本机代码时运行。

http://docs.python.org/c-api/init.html#thread-state-and-the-global-interpreter-lock

解锁 GIL 后,您无法访问 Python。释放 GIL,运行阻塞函数,然后当你知道你有东西要返回 Python 时,重新获取它。

如果你不这样做,那么当你的本机阻塞正在进行时,没有其他 Python 线程可以执行。如果这是供应商提供的 Python 模块,则在本机阻塞活动期间未能释放 GIL 是一个错误。

请注意,如果您接收到许多数据包,并且实际上在 Python 中处理它们,那么其他线程应该仍然运行。实际运行 Python 代码的多个线程不会并行运行,但它会经常在线程之间切换,让它们都有机会运行。如果本机代码在未释放 GIL 的情况下阻塞,这将不起作用。

编辑:我看到你提到这是一个供应商提供的库。如果您没有源代码,可以快速查看他们是否正在释放 GIL:在没有 USB 活动发生时启动 ReadUSBDevice 线程,因此 ReadUSBDevice 只是坐在那里等待数据。如果他们正在释放 GIL,其他线程应该可以畅通无阻地运行。如果不是,它将阻塞整个解释器。那将是一个严重的错误。

于 2009-07-30T09:15:37.650 回答
0

我觉得卖家说的没错。假设这是 CPython,没有真正的并行线程;一次只能执行一个线程。这是因为全局解释器锁的实现。

您可以通过使用multiprocessing模块获得可接受的解决方案,该模块通过产生真正的子进程有效地避开了垃圾收集器的锁定。

另一种可能有帮助的可能性是修改调度程序的切换行为

于 2009-07-30T09:04:40.450 回答