这种模式出现了很多,但我找不到直接的答案。
非关键,不友好的计划可能会
while(True):
# do some work
使用其他技术和平台,如果你想允许这个程序运行热(尽可能多地使用CPU周期)但是要礼貌 - 允许其他热门程序有效地减慢我的速度,你经常写:
while(True):
#do some work
time.sleep(0)
我已经阅读了有关后一种方法是否会在python上运行的相互矛盾的信息,在Linux机器上运行。它是否导致上下文切换,导致我上面提到的行为?
编辑:为了什么值得,我们尝试在Apple OSX中进行一些小实验(没有方便的linux盒子)。这个盒子有4个内核和超线程,所以我们只用一个来编写8个程序while(True):
i += 1
正如预期的那样,活动监视器将8个进程中的每个进程显示为占用超过95%的CPU(显然有4个内核和超线程,总共获得800%)。然后,我们制定了第九个这样的计划。现在所有9人都跑了85%左右。现在杀死第九个人并用
启动一个程序while(True):
i += 1
time.sleep(0)
我希望这个过程使用接近0%而另外8个将运行95%。但相反,所有九个人都跑了85%左右。所以在Apple OSX上,sleep(0)似乎没有效果。
答案 0 :(得分:21)
我从来没有想过这个,所以我写了这个剧本:
import time
while True:
print "loop"
time.sleep(0.5)
就像测试一样。使用strace -o isacontextswitch.strace -s512 python test.py
运行它会在循环中显示此输出:
write(1, "loop\n", 5) = 5
select(0, NULL, NULL, NULL, {0, 500000}) = 0 (Timeout)
write(1, "loop\n", 5) = 5
select(0, NULL, NULL, NULL, {0, 500000}) = 0 (Timeout)
write(1, "loop\n", 5) = 5
select(0, NULL, NULL, NULL, {0, 500000}) = 0 (Timeout)
write(1, "loop\n", 5) = 5
select(0, NULL, NULL, NULL, {0, 500000}) = 0 (Timeout)
write(1, "loop\n", 5)
select()
是一个系统调用,所以是的,你是上下文切换(从技术上讲,当你改为内核空间时,实际上并不需要上下文切换,但是如果你有其他进程正在运行,你说的是什么除非您准备好读取文件描述符的数据,否则其他进程可以运行到内核中,以便执行此操作。有趣的是,延迟是在stdin上选择。这允许python在他们希望的情况下中断你对ctrl+c
输入等事件的输入,而不必等待代码超时 - 我认为它非常整洁。
我应该注意,同样适用于time.sleep(0)
,但传入的时间参数是{0,0}
。并且旋转锁定对于除了非常短的延迟之外的任何事情都不是很理想 - multiprocessing
和threads
提供了等待事件对象的能力。
编辑:所以我看看确切知道linux的用途。 do_select
(fs\select.c
)中的实施进行了此检查:
if (end_time && !end_time->tv_sec && !end_time->tv_nsec) {
wait = NULL;
timed_out = 1;
}
if (end_time && !timed_out)
slack = select_estimate_accuracy(end_time);
换句话说,如果提供了结束时间且两个参数均为零(!0 = 1且在C中计算为真),则等待设置为NULL并且选择被视为超时。但是,这并不意味着该功能会返回给您;它遍历您拥有的所有文件描述符并调用cond_resched
,从而可能允许另一个进程运行。换句话说,发生的事情完全取决于调度程序;如果您的进程与其他进程相比占用CPU时间,则可能会发生上下文切换。如果没有,您所在的任务(内核do_select函数)可能会一直持续到完成。
答案 1 :(得分:11)
我认为你已经从@Ninefingers得到了答案,但在这个答案中我们将尝试深入研究python源代码。
首先,py time
模块在C中实现,要查看time.sleep
函数实现,您可以查看Modules/timemodule.c。正如您所看到的(并且没有获取所有平台特定的详细信息),此函数会将调用委托给floatsleep函数。
现在floatsleep
旨在在不同的平台上工作,但是只要有可能,行为就会被设计为类似的,但是因为我们只对类似unix的平台感兴趣,所以我们应该检查that part only :
...
Py_BEGIN_ALLOW_THREADS
sleep((int)secs);
Py_END_ALLOW_THREADS
您可以看到floatsleep
正在呼叫C
睡眠并且来自sleep man page:
sleep()函数将导致调用线程被挂起 从执行到指定的实时秒数 通过参数秒已经过去或......
但是等一下我们没忘记GIL吗?
这就是Py_BEGIN_ALLOW_THREADS
和Py_END_ALLOW_THREADS
宏出现的地方(如果您对这两个宏的定义感兴趣,请查看Include/ceval.h),上面的C代码可以使用这两个宏:
Save the thread state in a local variable.
Release the global interpreter lock.
... Do some blocking I/O operation ... (call sleep in our case)
Reacquire the global interpreter lock.
Restore the thread state from the local variable.
在the c-api doc中可以找到有关这两个宏的更多信息。
希望这有用。
答案 2 :(得分:7)
您基本上是在试图篡夺OS CPU调度程序的工作。简单地调用os.nice(100)
来通知调度程序您的优先级非常低,以便它可以正常工作可能会好得多。