python中的调度问题

2024-10-01 07:44:52 发布

您现在位置:Python中文网/ 问答频道 /正文

我使用python将一个硬件usb嗅探器设备与供应商提供的pythonapi接口,并尝试在一个无限循环的单独线程中从该设备读取(usb包)(这很好)。问题是我的主循环似乎再也不会被调度(我的read循环得到了所有的关注)。在

代码看起来很像:

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包大约一秒钟。然而,我看到的只是“sleep1”,然后usb_dump()过程将永远运行。如果我取消注释“时间。睡觉usb_dump()过程内部循环中的(0.001)“语句开始以我预期的方式工作,但是python代码就无法跟上所有传入的包:-(

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

«However, it seems like you are experiencing some nuances when using threading in Python. By putting the time.sleep in the DumpThread thread, you are explicitly signaling to the Python threading system to give up control. Otherwise, it is up the Python interpreter to determine when to switch threads and it usually does that after a certain number of byte code instructions have been executed.»

有人能确认python是这里的问题吗?有没有其他方法使DumpThread释放控制?还有别的主意吗?在


Tags: theto代码硬件timepacketdeviceit
3条回答

如果您的供应商是纯python代码,那么您的供应商是对的;但是,C扩展可能会释放GIL,因此允许实际的多线程处理。在

尤其是,时间。睡觉是否发布了GIL(您可以直接从源代码中检查,here-请看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设备未传递到线程。您需要将其作为参数传递或(argh!)告诉线程从全局命名空间获取它。在
  • 而不是强迫系统出口(),最好只发出停止线程的信号,然后关闭USB设备。我怀疑您的代码可能会遇到一些多线程问题,就像现在这样。在
  • 如果你只需要定期投票,线程。计时器上课对你来说可能是个更好的解决办法。在

[Update]关于最新的一点:如评论中所述,我认为Timer更适合您函数的语义(定期轮询),并自动避免GIL未由供应商代码发布的问题。在

我假设您编写了一个pythoncmodule,它公开ReadUSBDevice函数,它的目的是在接收到USB包之前阻塞它,然后返回它。在

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

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

虽然您已经解锁了GIL,但您不能访问Python。释放GIL,运行blocking函数,然后当您知道有东西要返回Python时,重新获取它。在

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

请注意,如果您接收到许多包,并且实际使用Python处理它们,那么其他线程仍然应该运行。实际上运行Python代码的多个线程不会并行运行,但它会频繁地在线程之间切换,给它们提供运行的机会。如果本机代码在没有释放GIL的情况下阻塞,这就不起作用了。在

编辑:我看到你提到这是一个供应商提供的库。如果你没有source,一个快速查看他们是否正在释放GIL的方法:在没有USB活动的情况下启动ReadUSBDevice线程,所以ReadUSBDevice只是坐在那里等待数据。如果他们正在释放GIL,其他线程应该可以畅通无阻地运行。如果他们不这样做,整个口译员都会被挡住。那将是一个严重的错误。在

我认为卖主是对的。假设这是CPython,没有真正的并行线程;一次只能执行一个线程。这是因为global interpreter lock的实现。在

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

另一种可能有帮助的方法是修改调度程序的switching behaviour。在

相关问题 更多 >