如何在python中定期运行函数
- 2025-03-26 09:10:00
- admin 原创
- 11
问题描述:
我有一个简单的节拍器在运行,不知何故,当它的节拍较低时,它很好,但当它的节拍较高时,它就不一致且不稳定。我不知道发生了什么。我想尝试使用某种东西定期运行它。有办法吗?
这是我的代码:
class thalam():
def __init__(self,root,e):
self.lag=0.2
self.root=root
self.count=0
self.thread=threading.Thread(target=self.play)
self.thread.daemon=True
self.tempo=60.0/120
self.e=e
self.pause=False
self.tick=open("tick.wav","rb").read()
self.count=0
self.next_call = time.time()
def play(self):
if self.pause:
return
winsound.PlaySound(self.tick,winsound.SND_MEMORY)
self.count+=1
if self.count==990:
self.thread=threading.Thread(target=self.play)
self.thread.daemon=True
self.thread.start()
return
self.next_call+=self.tempo
new=threading.Timer(self.next_call-time.time(),self.play)
new.daemon=True
new.start()
def stop(self):
self.pause=True
winsound.PlaySound(None,winsound.SND_ASYNC)
def start(self):
self.pause=False
def settempo(self,a):
self.tempo=a
class Metronome(Frame):
def __init__(self,root):
Frame.__init__(self,root)
self.first=True
self.root=root
self.e=Entry(self)
self.e.grid(row=0,column=1)
self.e.insert(0,"120")
self.play=Button(self,text="Play",command=self.tick)
self.play.grid(row=1,column=1)
self.l=Button(self,text="<",command=lambda:self.inc("l"))
self.l.grid(row=0,column=0)
self.r=Button(self,text=">",command=lambda:self.inc("r"))
self.r.grid(row=0,column=2)
def tick(self):
self.beat=thalam(root,self.e)
self.beat.thread.start()
self.play.configure(text="Stop",command=self.notick)
def notick(self):
self.play.configure(text="Start",command=self.tick)
self.beat.stop()
def inc(self,a):
if a=="l":
try:
new=str(int(self.e.get())-5)
self.e.delete(0, END)
self.e.insert(0,new)
self.beat.settempo(60.0/(int(self.e.get())))
except:
print "Invalid BPM"
return
elif a=="r":
try:
new=str(int(self.e.get())+5)
self.e.delete(0, END)
self.e.insert(0,new)
self.beat.settempo((60.0/(int(self.e.get()))))
except:
print "Invalid BPM"
return
解决方案 1:
播放声音来模拟普通节拍器不需要“实时”功能。
看起来您使用 Tkinter 框架来创建 GUI。root.after()
允许您延迟调用函数。您可以使用它来实现滴答:
def tick(interval, function, *args):
root.after(interval - timer() % interval, tick, interval, function, *args)
function(*args) # assume it doesn't block
tick()
每毫秒function
按给定时间运行。单个刻度的持续时间受精度影响,但从长远来看,稳定性仅取决于功能。args
`intervalroot.after()
timer()`
这是一个打印一些统计数据(240
每分钟心跳次数)的脚本:
#!/usr/bin/env python
from __future__ import division, print_function
import sys
from timeit import default_timer
try:
from Tkinter import Tk
except ImportError: # Python 3
from tkinter import Tk
def timer():
return int(default_timer() * 1000 + .5)
def tick(interval, function, *args):
root.after(interval - timer() % interval, tick, interval, function, *args)
function(*args) # assume it doesn't block
def bpm(milliseconds):
"""Beats per minute."""
return 60000 / milliseconds
def print_tempo(last=[timer()], total=[0], count=[0]):
now = timer()
elapsed = now - last[0]
total[0] += elapsed
count[0] += 1
average = total[0] / count[0]
print("{:.1f} BPM, average: {:.0f} BPM, now {}"
.format(bpm(elapsed), bpm(average), now),
end='
', file=sys.stderr)
last[0] = now
interval = 250 # milliseconds
root = Tk()
root.withdraw() # don't show GUI
root.after(interval - timer() % interval, tick, interval, print_tempo)
root.mainloop()
节奏仅相差一个节拍:在我的计算机上为 240±1。
模拟情况如下asyncio
:
#!/usr/bin/env python3
"""Metronome in asyncio."""
import asyncio
import sys
async def async_main():
"""Entry point for the script."""
timer = asyncio.get_event_loop().time
last = timer()
def print_tempo(now):
nonlocal last
elapsed = now - last
print(f"{60/elapsed:03.1f} BPM", end="
", file=sys.stderr)
last = now
interval = 0.250 # seconds
while True:
await asyncio.sleep(interval - timer() % interval)
print_tempo(timer())
if __name__ == "__main__":
asyncio.run(async_main())
请参阅演讲:Łukasz Langa - AsyncIO + 音乐
解决方案 2:
由于处理器需要与其他程序共享自身,因此执行任何需要时间精度的操作都非常困难。不幸的是,对于时间关键型程序,操作系统可以随时切换到另一个进程。这可能意味着它可能要经过明显的延迟后才能返回到您的程序。在导入时间之后使用time.sleep是一种更一致的方式来平衡蜂鸣声之间的时间,因为处理器切换的“理由”较少。虽然 Windows 上的睡眠默认粒度为 15.6 毫秒,但我认为您不需要播放超过 64Hz 的节拍。此外,您似乎正在使用多线程来尝试解决您的问题,但是,线程的 Python 实现有时会强制线程按顺序运行。这使得切换出您的进程的情况更加糟糕。
我认为最好的解决方案是生成包含所需频率的节拍器蜂鸣声的声音数据。然后,您可以以操作系统可以理解的方式播放声音数据。由于系统知道如何以可靠的方式处理声音,因此您的节拍器可以正常工作。
很抱歉让您失望,但时间关键型应用程序非常困难,除非您愿意亲自处理您正在使用的系统。
解决方案 3:
我想告诉你,由于竞争条件,在时间方面你无法精确地控制线程(即使你正在使用锁和信号量!)。甚至我也遇到过这个问题。