在python中安排问题

时间:2009-07-30 08:55:10

标签: python multithreading scheduling

我正在使用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()程序永远运行。如果我在usb_dump()过程的内部循环中取消注释“time.sleep(0.001)”语句,事情会按照我期望的方式开始工作,但是python代码变得无法跟上所有进入的数据包: - (

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

  

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

有人可以确认python是问题吗?有没有其他方法来进行DumpThread释放控制?还有其他想法吗?

3 个答案:

答案 0 :(得分:3)

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

特别是time.sleep 确实发布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_device未传递给线程。您需要将其作为参数传递或(argh!)告诉线程从全局命名空间获取它。
  • 而不是强制sys.exit(),最好只是通知线程停止,然后关闭USB设备。我怀疑你的代码可能会出现一些多线程问题,就像现在一样。
  • 如果您只需要定期轮询,则threading.Timer类可能是更好的解决方案。

[更新]关于最新观点:正如评论中所述,我认为Timer更适合您的函数语义(定期民意调查),并会自动避免问题GIL没有被供应商代码发布。

答案 1 :(得分: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,其他线程应该不受阻碍地运行。如果他们不是,它将阻止整个翻译。那将是一个严重的错误。

答案 2 :(得分:0)

我认为供应商是正确的。假设这是CPython,没有真正的并行线程;一次只能执行一个线程。这是因为global interpreter lock

的实施

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

另一种可能有用的可能是修改调度程序的switching behaviour