一种几乎正确地定期触发功能的方法 [英] A way to almost correctly trigger a function periodically
问题描述
我想定期触发一个函数,比如说每 5 秒.该函数不会消耗很多CPU时间,所以我预计即使是阻塞函数也不会有问题.我知道 python 永远不会成为实时语言,即使它运行在实时操作系统上,我只是想避免延迟显着增加和不同步我的数据采集.
I would like to trigger a function periodically, lets say, each 5s. The function will not consume many CPU time, so I expect that even if it is a blocking function it will not be a problem. I know that python will never be a real-time language, even if it run on a real-time os, I just want to avoid delays to significantly add up and desynchronize my data acquisition.
我比较了两种不同的解决方法,我有点惊讶.使用 threading
模块(在 SO 上找到的解决方案),会有一个延迟加起来,即使在 100 秒的短时间范围内,这也很重要.
I have compared two different ways to resolve it and I am a little surprised. Using threading
module (solution found on SO), there is a a delay adding up, and this is significant even on a short horizon of 100s.
import time
import threading
# Function to trigger
def q(t0, t1):
print("{}: {}".format(t1, t1-t0))
# Scheduler:
def f(t):
t0 = time.clock()
q(t, t0)
threading.Timer(5, f, (t0, )).start()
f(time.clock())
输出为:
1.2439980979269082e-06: 6.219990489634541e-07
5.003068943307586: 5.003067699309487
10.009677372203297: 5.006608428895712
15.017115547830327: 5.00743817562703
20.02463987032564: 5.007524322495312
25.03211007890369: 5.007470208578049
30.039602057448455: 5.007491978544767
35.04705640505075: 5.007454347602298
40.0545011116678: 5.0074447066170436
45.06215045597195: 5.007649344304156
50.069445571817724: 5.007295115845771
55.076933507368665: 5.007487935550941
60.0844149119296: 5.007481404560934
65.09188791950338: 5.007473007573779
70.09936870206525: 5.007480782561871
75.10685632661668: 5.00748762455143
80.11432187020186: 5.007465543585184
85.12207042335432: 5.007748553152453
90.12924456038506: 5.007174137030745
95.13673964892507: 5.007495088540011
100.1442070585074: 5.007467409582333
105.15168068808023: 5.007473629572829
当我使用老式微控制器的 C 风格代码解决我的问题时:
When I solve my problem using old-fashion C style code for micro-controller:
import time
# Function to trigger:
def q(t0, t1):
print("{}: {}".format(t1, t1-t0))
# Scheduler:
t0 = time.clock()
while True:
t1 = time.clock()
if (t1-t0)>=5:
q(t0, t1)
t0 = t1
我明白了:
5.0000009329985735: 5.0
10.000001243998097: 5.000000310999524
15.000001243998097: 5.0
20.0000012439981: 5.000000000000002
25.0000012439981: 5.0
30.0000012439981: 5.0
35.0000012439981: 5.0
40.0000012439981: 5.0
45.0000012439981: 5.0
50.0000012439981: 5.0
55.0000012439981: 5.0
60.0000012439981: 5.0
65.0000012439981: 5.0
70.0000012439981: 5.0
75.0000012439981: 5.0
80.0000012439981: 5.0
85.0000012439981: 5.0
90.0000012439981: 5.0
95.0000012439981: 5.0
100.0000012439981: 5.0
105.0000012439981: 5.0
这似乎更可靠.我知道这些显示中可能存在浮点问题,但它无法解释两种解决方案之间的区别.
Which seems to be really more reliable. I know that there might be a float point issue in those displays, but it cannot explain the difference between the two solutions.
- 是不是因为
threading
模块依赖于time.sleep
函数?
- Is it because
threading
module relies ontime.sleep
function?
在我看来,第二个选项更好,因为它避免了线程和递归,即使它使用了无限循环.- 有没有更好的方法来实现这个目标?
In my point of view, I would say, second option is better because it avoids thread and recursion, even if it uses a endless loop. - Is there a better way to achieve that goal?
更深入地解决我的问题:- 如何在定义的时间戳上同步触发器?sched
模块会有帮助吗?
Going deeper in my problem:
- How can I synchronize the trigger on a defined timestamps? Will the sched
module be helpful?
推荐答案
问题中描述的第二种方法会导致繁忙循环(吃掉所有 CPU).
The second approach described in the question makes a busy loop (eating all CPU).
另一种比使用线程更简单的方法是使用旧的 select
系统调用:
Another simpler approach than using threads is to use the good old select
system call:
import time
import select
import socket # win
s = socket.socket() # win
# Function to trigger:
def q(t0, t1):
print("{}: {}".format(t1, t1-t0))
# Scheduler:
t0 = time.time()
while True:
select.select([s],[],[],5) #wait for 5 seconds (dummy socket for win)
t1 = time.time()
q(t0, t1)
t0 = t1
结果:
1441792007.3: 5.00524997711
1441792012.3: 5.00554990768
1441792017.31: 5.00520896912
1441792022.31: 5.00508904457
1441792027.32: 5.00518012047
1441792032.32: 5.00523996353
1441792037.33: 5.00523781776
此外,time.clock
在 Linux 上对我不起作用.文档说:
Also, time.clock
doesn't work for me on Linux. Documentation says:
clock() 方法以浮点数形式返回当前处理器时间,在 Unix 上以秒表示.精度取决于同名 C 函数的精度,但无论如何,这是用于对 Python 或计时算法进行基准测试的函数.
The method clock() returns the current processor time as a floating point number expressed in seconds on Unix. The precision depends on that of the C function of the same name, but in any case, this is the function to use for benchmarking Python or timing algorithms.
在 Windows 上,此函数根据 Win32 函数 QueryPerformanceCounter 以浮点数形式返回自第一次调用此函数以来经过的挂钟秒数.
On Windows, this function returns wall-clock seconds elapsed since the first call to this function, as a floating point number, based on the Win32 function QueryPerformanceCounter.
也许您使用的是 Windows?或者你在 Linux 上,但作为第二个例子是使 CPU 忙碌,time.clock
确实给出了一个数字,而对于我的代码,它始终为 0,因为没有真正涉及 CPU 周期.
Maybe you're on Windows ? Or you're on Linux but as the second example is
making CPU busy, time.clock
really gives a number whereas with my code it is always 0 since no CPU cycles are really involved.
这篇关于一种几乎正确地定期触发功能的方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!