大家好。为了这个想法,我想编写某种控制台秒表。吃
import os
import asyncio
import time
import logging
logging.basicConfig(filename = "mylog.log", level=logging.DEBUG)
class Timer():
def __init__(self) -> None:
self.minute = 0
self.seconds = 0
self.deciseconds = 0
def add_deciseconds(self):
self.deciseconds += 1
if self.deciseconds >= 10:
self.seconds += 1
self.deciseconds = 0
if self.seconds >= 60:
self.seconds = 0
self.minute += 1
def get_time(self):
return (self.minute, self.seconds, self.deciseconds)
async def tick(my_timer: Timer):
my_timer.add_deciseconds()
os.system("cls")
m , s, ss = my_timer.get_time()
print(f"{m} : {s} : {ss}")
async def main(tim):
await tick(tim)
if __name__ == '__main__':
my_timer = Timer()
while True:
start = time.time()
logging.debug(start)
asyncio.run(main(my_timer))
time.sleep(1)
end = time.time()
logging.debug(end)
logging.debug(end - start)
logging.debug('------------------')
日志和异步已经是解决问题的尝试。
问题:time.sleep(1)
等待超过 1 秒。为了进行测试,我打开了这个应用程序,几秒钟后打开了手机上的秒表。大约第二分钟,手机上的秒表开始追上。
我对异步编程不太了解,但我认为输出到时间终端并加一的代码使程序仍然等待,所以我尝试使函数异步。受到文档中模板的启发
日志看起来像这样:
DEBUG:root:1699353460.6091332
DEBUG:asyncio:Using proactor: IocpProactor
DEBUG:root:1699353461.6302617
DEBUG:root:1.0211284160614014 # Разница между старт и стоп больше 1 секунды
DEBUG:root:------------------
DEBUG:root:1699353461.6313007
DEBUG:asyncio:Using proactor: IocpProactor
DEBUG:root:1699353462.666385
DEBUG:root:1.0350842475891113 # Разница между старт и стоп больше 1 секунды
DEBUG:root:------------------
我读过各种文章,包括其他人对 SO 的问题(一个例子)。他们说该误差是可以接受的,但他们谈论的误差约为 0.00001 或更小。
我尝试在没有任何计时器的情况下运行循环:
while True:
start = time.time()
logging.debug(start)
time.sleep(1)
end = time.time()
logging.debug(end)
logging.debug(end - start)
logging.debug('------------------')
在这种情况下,日志中的数字已经更加准确
DEBUG:root:1699355102.4560356
DEBUG:root:1699355103.4560683
DEBUG:root:1.000032663345337
DEBUG:root:------------------
DEBUG:root:1699355103.457032
DEBUG:root:1699355104.4571276
DEBUG:root:1.0000956058502197
DEBUG:root:------------------
如果不进行日志记录,计时器的运行速度仍然会慢一些。
我不知道到底是什么减慢了我的程序?
os.system("cls")
第一种情况和logging.debug
第二种情况下的调用会减慢您的程序的速度。time.sleep()
给你的代码增加了延迟,但是你的代码本身并没有在零时间内运行,导致计时器逐渐落后于实时。即使是简单的计时
sleep
将显示实际延迟超过 1 秒(它为我显示
1.000610113143921
)。随着样本之间的代码增多(以及速度较慢的代码,例如 I/O,包括 cls),额外的延迟会更大,并且循环中的滞后会逐渐增加。另外,操作系统不保证延迟的绝对准确性;某些进程可能会长时间占用内核,可能不会在计划的时间从“睡眠”返回。
假设没有繁重的后台进程,您可以考虑迭代的实际执行时间来重新计算延迟。好吧,显示的是实际经过的时间(根据电脑计时器),而不是手动计算的。具有延迟重新计算功能的计时器原型,每秒应执行 10 次迭代:
结论:
可以看出,迭代之间保持了大约+-0.001的精度差距。
在 Linux 上测试;在 Windows 上结果可能不同(似乎在那里你不能以优于 0.01 秒的精度进行延迟,因为这样计时器的精度可能会更差)。
我认为将计时器放在异步函数中没有多大意义。如果在同一事件循环中执行其他函数,这将增加额外的随机误差。如有必要,请在单独的线程中运行它。