A way to almost correctly trigger a function periodically

匆匆过客 提交于 2019-12-11 08:33:46

问题


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.

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())

Output is:

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

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

I get:

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.

  • 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?

Going deeper in my problem: - How can I synchronize the trigger on a defined timestamps? Will the sched module be helpful?


回答1:


The second approach described in the question makes a busy loop (eating all CPU).

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

Results:

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

Also, time.clock doesn't work for me on Linux. Documentation says:

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.

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.

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.



来源:https://stackoverflow.com/questions/32475434/a-way-to-almost-correctly-trigger-a-function-periodically

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!