Python中,threading.Thread如何让出其时间片?

56

我有一个线程正在轮询一块硬件设备。

while not hardware_is_ready():
    pass
process_data_from_hardware()

但是还有其他线程(和进程!)可能有事情要做。如果是这样,我不想在每个指令中都消耗cpu检查硬件。我已经有一段时间没有处理线程了,当我处理时不是用的Python,但我相信大多数线程库都有一个yield函数或类似的函数,允许线程告诉调度程序“给其他线程一个机会。”

while not hardware_is_ready():
    threading.yield()          # This function doesn't exist.
process_data_from_hardware()

但我在线程文档中找不到类似的内容。Python确实有一个yield语句,但我相信那完全是另一回事(与生成器有关)。

这里应该怎么做才正确呢?

4个回答

91

time.sleep(0)足以让出控制权,无需使用正的epsilon值。事实上,time.sleep(0)的意思是“让出给任何其他准备就绪的线程”。


6
这个有文献资料吗?我认为这可能是真的——我在其他语言中看到过——但我在Python文档中没有找到提到它的地方。 - Tom Future
5
例如,http://docs.python.org/library/sched.html 中的一句话:"在多线程应用程序中,每次事件运行后,为了让其他线程有机会运行,delayfunc将使用参数0进行调用。" (这里通常是time.sleep)。 我同意如果在time模块中能更直接地记录这个细节会更好;-) - Alex Martelli
5
Windows用户:将设置为0对我无效,time.sleep(0.0001)有效。 - driedler
这在我的Windows 7上使用Python 3.6.4无法工作。 - waszil

14

阅读全局解释器锁(GIL)相关内容。

例如:http://jessenoller.com/2009/02/01/python-threads-and-the-global-interpreter-lock/

此外:http://www.pyzine.com/Issue001/Section_Articles/article_ThreadingGlobalInterpreter.html

如果必须进行忙等待(例如轮询设备),请在代码中执行此操作。

time.sleep( 0.0001 )
这将导致线程调度程序。同时,我在http://homepage.mac.com/s_lott/iblog/architecture/C551260341/E20081031204203/index.html 中收集了一些注释和参考资料。

2
我不是专家,但我对GIL有一些了解。你能给我一点提示,特别是与这个问题有关的内容吗?目前我会使用time.sleep(epsilon),但这种方法并不令人满意,因为我并没有特定的时间间隔需要睡眠。 - Tom Future
如果您的硬件无法发送中断,这是唯一的解决方案。 - Unknown
Python如何进行抢占式yielding而不使用time.sleep()?在什么条件下会发生yield? - CMCDragonkai
4
三个链接似乎都失效了。 - trincot

6

time.sleep(0)可能不足以满足需求:

我曾经遇到过类似的问题,最后发现time.sleep(0)并不总是起作用,而time.sleep(0.0001)则有效。 在我的情况下,我使用的是标准版的Python,可以在python.org上找到。 通过查看C代码中sleep函数的实现,可以发现在调用sleep(0)sleep(非0值)之间有微小的差别。

查看timemodule.c中的pysleep,你会发现在第一种情况下:

if (ul_millis == 0 || !_PyOS_IsMainThread()) {
  Py_BEGIN_ALLOW_THREADS
  Sleep(ul_millis);
  Py_END_ALLOW_THREADS
  break;
}

这被称为“允许线程”,它释放了GIL。在这种情况下,Sleep(0)等同于std::this_thread::yield(),然后再次声明GIL。 在处理非零值的代码中,除了实际的睡眠/等待之外,还会调用PyErr_CheckSignals - 正如其文档所述“检查是否已向进程发送信号,如果是,则调用相应的信号处理程序”。这可能解释了为什么在处理硬件时,time.sleep(0)不足够。因此,这可能是cpython实现方面的一个失误。这就是为什么time.sleep(0.0001)能够奇妙地起作用。


4
如果你在*nix上进行这个操作,你可能会发现 select 库很有用。 Kamaela 也有一些组件可能对您有用,但可能需要进行一些范式转换。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接