一种几乎正确地定期触发功能的方法 [英] A way to almost correctly trigger a function periodically

查看:30
本文介绍了一种几乎正确地定期触发功能的方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想定期触发一个函数,比如说每 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 on time.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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆