控制循环速度
我现在在大学学物理,同时也在学习Python,算是一个小爱好。
为了同时练习这两样,我想写一个简单的“物理引擎”,用来根据x、y和z坐标计算物体的运动。我现在只打算把运动结果以文字形式输出(至少目前是这样!),但我希望位置的更新是实时的。
为此,我需要每秒更新物体的位置大约一百次,并把它打印到屏幕上。所以每10毫秒程序就要打印一次当前的位置。
假设计算执行需要2毫秒,那么循环就得等8毫秒,才能打印并重新计算下一个位置。
那么,构建这样一个循环的最佳方法是什么呢?每秒100次更新频率合适吗,还是说你觉得应该慢一点,比如每秒25次?
3 个回答
1
这里有个警告。你可能不能在一个非实时系统上期待实时的反应。sleep
这一类的调用保证你至少会等待一段时间,但实际上可能会让你等得更久。
所以,一旦你从睡眠状态中返回,记得查询一下当前时间,然后再进行“未来”的计算(要考虑到计算所花的时间)。
2
因为你无法提前知道每次循环会花多长时间,所以你需要一种事件驱动的循环方式。一个可能的解决方案是使用 twisted
模块,它是基于一种叫做反应器模式的设计。
from twisted.internet import task
from twisted.internet import reactor
delay = 0.1
def work():
print "called"
l = task.LoopingCall(work)
l.start(delay)
reactor.run()
不过,正如之前提到的,不要指望它能真正做到实时响应。
2
在Python中,最基本的等待方法是先用import time
导入时间模块,然后使用time.sleep
来让程序暂停一段时间。那么问题来了,暂停多久呢?这就要看你想怎么处理循环中错过的时间了。下面的实现方式会尝试在错过目标时间后赶上去。
import time
import random
def doTimeConsumingStep(N):
"""
This represents the computational part of your simulation.
For the sake of illustration, I've set it up so that it takes a random
amount of time which is occasionally longer than the interval you want.
"""
r = random.random()
computationTime = N * (r + 0.2)
print("...computing for %f seconds..."%(computationTime,))
time.sleep(computationTime)
def timerTest(N=1):
repsCompleted = 0
beginningOfTime = time.clock()
start = time.clock()
goAgainAt = start + N
while 1:
print("Loop #%d at time %f"%(repsCompleted, time.clock() - beginningOfTime))
repsCompleted += 1
doTimeConsumingStep(N)
#If we missed our interval, iterate immediately and increment the target time
if time.clock() > goAgainAt:
print("Oops, missed an iteration")
goAgainAt += N
continue
#Otherwise, wait for next interval
timeToSleep = goAgainAt - time.clock()
goAgainAt += N
time.sleep(timeToSleep)
if __name__ == "__main__":
timerTest()
要注意的是,在普通的操作系统上,你肯定会错过你想要的时间,所以像这样的处理是很有必要的。即使使用像tulip和twisted这样的异步框架,在普通操作系统上也无法保证时间的准确性。