我正在使用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释放控制?还有其他想法吗?
答案 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)
最后,关于您发布的代码的几点说明:
[更新]关于最新观点:正如评论中所述,我认为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。