Est-ce que Python a une fonction similaire à la setInterval()
de JavaScript?
Merci
Cela pourrait être le bon extrait que vous cherchiez:
import threading
def set_interval(func, sec):
def func_wrapper():
set_interval(func, sec)
func()
t = threading.Timer(sec, func_wrapper)
t.start()
return t
Restez simple et agréable, je ne sais pas pourquoi toutes les réponses rendent les choses si complexes:
import threading
def setInterval(func,time):
e = threading.Event()
while not e.wait(time):
func()
def foo():
print "hello"
# using
setInterval(foo,5)
# output:
hello
hello
.
.
.
EDIT: Ce code est non bloquant
import threading
class ThreadJob(threading.Thread):
def __init__(self,callback,event,interval):
'''runs the callback function after interval seconds
:param callback: callback function to invoke
:param event: external event for controlling the update operation
:param interval: time in seconds after which are required to fire the callback
:type callback: function
:type interval: int
'''
self.callback = callback
self.event = event
self.interval = interval
super(ThreadJob,self).__init__()
def run(self):
while not self.event.wait(self.interval):
self.callback()
event = threading.Event()
def foo():
print "hello"
k = ThreadJob(foo,event,2)
k.start()
print "It is non-blocking"
Ceci est une version où vous pouvez démarrer et arrêter. Il n’est pas bloquant. Il n’ya pas non plus de petit problème car une erreur de temps d’exécution n’est pas ajoutée (important pour une exécution de longue durée avec un intervalle très court comme audio par exemple)
import time, threading
StartTime=time.time()
def action() :
print('action ! -> time : {:.1f}s'.format(time.time()-StartTime))
class setInterval :
def __init__(self,interval,action) :
self.interval=interval
self.action=action
self.stopEvent=threading.Event()
thread=threading.Thread(target=self.__setInterval)
thread.start()
def __setInterval(self) :
nextTime=time.time()+self.interval
while not self.stopEvent.wait(nextTime-time.time()) :
nextTime+=self.interval
self.action()
def cancel(self) :
self.stopEvent.set()
# start action every 0.6s
inter=setInterval(0.6,action)
print('just after setInterval -> time : {:.1f}s'.format(time.time()-StartTime))
# will stop interval in 5s
t=threading.Timer(5,inter.cancel)
t.start()
La sortie est:
just after setInterval -> time : 0.0s
action ! -> time : 0.6s
action ! -> time : 1.2s
action ! -> time : 1.8s
action ! -> time : 2.4s
action ! -> time : 3.0s
action ! -> time : 3.6s
action ! -> time : 4.2s
action ! -> time : 4.8s
Le module sched
fournit ces capacités pour le code Python général. Toutefois, comme le suggère sa documentation, si votre code est multithread, il serait peut-être plus judicieux d'utiliser la classe threading.Timer
à la place.
Change Nailxx réponds un peu et tu as la réponse!
from threading import Timer
def hello():
print "hello, world"
Timer(30.0, hello).start()
Timer(30.0, hello).start() # after 30 seconds, "hello, world" will be printed
Je pense que c'est ce que vous recherchez:
#timertest.py
import sched, time
def dostuff():
print "stuff is being done!"
s.enter(3, 1, dostuff, ())
s = sched.scheduler(time.time, time.sleep)
s.enter(3, 1, dostuff, ())
s.run()
Si vous ajoutez une autre entrée au planificateur à la fin de la méthode de répétition, elle continuera.
Utils setInterval simples
from threading import Timer
def setInterval(timer, task):
isStop = task()
if not isStop:
Timer(timer, setInterval, [timer, task]).start()
def hello():
print "do something"
return False # return True if you want to stop
if __== "__main__":
setInterval(2.0, hello) # every 2 seconds, "do something" will be printed
La plupart des réponses ci-dessus n'arrêtent pas le thread correctement. En utilisant le cahier Jupyter, j’ai remarqué que lorsqu’une interruption explicite était envoyée, les threads étaient toujours en cours d’exécution et, pire encore, ils se multipliaient à partir d’un, 2, 4, etc. en cours d'exécution. gère les interruptions en exécutant une boucle infinie dans le thread principal pour écouter les événements SIGINT et SIGTERM
N'hésitez pas à suggérer des améliorations
import time
import threading
import signal
# Record the time for the purposes of demonstration
start_time=time.time()
class ProgramKilled(Exception):
"""
An instance of this custom exception class will be thrown everytime we get an SIGTERM or SIGINT
"""
pass
# Raise the custom exception whenever SIGINT or SIGTERM is triggered
def signal_handler(signum, frame):
raise ProgramKilled
# This function serves as the callback triggered on every run of our IntervalThread
def action() :
print('action ! -> time : {:.1f}s'.format(time.time()-start_time))
# https://stackoverflow.com/questions/2697039/python-equivalent-of-setinterval
class IntervalThread(threading.Thread) :
def __init__(self,interval,action, *args, **kwargs) :
super(IntervalThread, self).__init__()
self.interval=interval
self.action=action
self.stopEvent=threading.Event()
self.start()
def run(self) :
nextTime=time.time()+self.interval
while not self.stopEvent.wait(nextTime-time.time()) :
nextTime+=self.interval
self.action()
def cancel(self) :
self.stopEvent.set()
def main():
# Handle SIGINT and SIFTERM with the help of the callback function
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)
# start action every 1s
inter=IntervalThread(1,action)
print('just after setInterval -> time : {:.1f}s'.format(time.time()-start_time))
# will stop interval in 500s
t=threading.Timer(500,inter.cancel)
t.start()
# https://www.g-loaded.eu/2016/11/24/how-to-terminate-running-python-threads-using-signals/
while True:
try:
time.sleep(1)
except ProgramKilled:
print("Program killed: running cleanup code")
inter.cancel()
break
if __== "__main__":
main()
Récemment, j'ai le même problème que vous. Et je trouve ces soluences:
1. vous pouvez utiliser la bibliothèque: threading.Time (ceci a une introduction ci-dessus)} _
_ {2. vous pouvez utiliser la bibliothèque: sched (cette introduction est aussi présentée ci-dessus aussi))}
3. vous pouvez utiliser la bibliothèque: Advanced Python Scheduler(Recommander)
Mon module Python 3 jsinterval.py
sera utile! C'est ici:
"""
Threaded intervals and timeouts from JavaScript
"""
import threading, sys
__all__ = ['TIMEOUTS', 'INTERVALS', 'setInterval', 'clearInterval', 'setTimeout', 'clearTimeout']
TIMEOUTS = {}
INTERVALS = {}
last_timeout_id = 0
last_interval_id = 0
class Timeout:
"""Class for all timeouts."""
def __init__(self, func, timeout):
global last_timeout_id
last_timeout_id += 1
self.timeout_id = last_timeout_id
TIMEOUTS[str(self.timeout_id)] = self
self.func = func
self.timeout = timeout
self.threadname = 'Timeout #%s' %self.timeout_id
def run(self):
func = self.func
delx = self.__del__
def func_wrapper():
func()
delx()
self.t = threading.Timer(self.timeout/1000, func_wrapper)
self.t.name = self.threadname
self.t.start()
def __repr__(self):
return '<JS Timeout set for %s seconds, launching function %s on timeout reached>' %(self.timeout, repr(self.func))
def __del__(self):
self.t.cancel()
class Interval:
"""Class for all intervals."""
def __init__(self, func, interval):
global last_interval_id
self.interval_id = last_interval_id
INTERVALS[str(self.interval_id)] = self
last_interval_id += 1
self.func = func
self.interval = interval
self.threadname = 'Interval #%s' %self.interval_id
def run(self):
func = self.func
interval = self.interval
def func_wrapper():
timeout = Timeout(func_wrapper, interval)
self.timeout = timeout
timeout.run()
func()
self.t = threading.Timer(self.interval/1000, func_wrapper)
self.t.name = self.threadname
self.t.run()
def __repr__(self):
return '<JS Interval, repeating function %s with interval %s>' %(repr(self.func), self.interval)
def __del__(self):
self.timeout.__del__()
def setInterval(func, interval):
"""
Create a JS Interval: func is the function to repeat, interval is the interval (in ms)
of executing the function.
"""
temp = Interval(func, interval)
temp.run()
idx = int(temp.interval_id)
del temp
return idx
def clearInterval(interval_id):
try:
INTERVALS[str(interval_id)].__del__()
del INTERVALS[str(interval_id)]
except KeyError:
sys.stderr.write('No such interval "Interval #%s"\n' %interval_id)
def setTimeout(func, timeout):
"""
Create a JS Timeout: func is the function to timeout, timeout is the timeout (in ms)
of executing the function.
"""
temp = Timeout(func, timeout)
temp.run()
idx = int(temp.timeout_id)
del temp
return idx
def clearTimeout(timeout_id):
try:
TIMEOUTS[str(timeout_id)].__del__()
del TIMEOUTS[str(timeout_id)]
except KeyError:
sys.stderr.write('No such timeout "Timeout #%s"\n' %timeout_id)
CODE EDIT: Correction de la fuite de mémoire (repérée par @benjaminz). Maintenant TOUS les threads sont nettoyés à la fin. Pourquoi cette fuite se produit-elle? Cela se produit à cause des références implicites (voire explicites). Dans mon cas, TIMEOUTS
et INTERVALS
. Les délais d'attente s'auto-nettoient automatiquement (après ce correctif) car ils utilisent un wrapper de fonction qui appelle la fonction puis auto-tue. Mais comment cela se produit-il? Les objets ne peuvent être supprimés de la mémoire que si toutes les références sont également supprimées ou si le module gc
est utilisé. Expliquer: il n’existe aucun moyen de créer (dans mon code) des références non désirées aux délais/intervalles. Ils n’ont qu’UN SEUL référent: les TIMEOUTS
/INTERVALS
dict. Et, lorsqu'ils sont interrompus ou terminés (seuls les délais peuvent se terminer sans interruption), ils suppriment la seule référence existante à eux-mêmes: leur élément dict correspondant. Les classes sont parfaitement encapsulées à l'aide de __all__
, donc pas d'espace pour les fuites de mémoire.
La méthode ci-dessus ne l'a pas vraiment fait pour moi car j'avais besoin de pouvoir annuler l'intervalle. J'ai transformé la fonction en classe et ai proposé ce qui suit:
class setInterval():
def __init__(self, func, sec):
def func_wrapper():
self.t = threading.Timer(sec, func_wrapper)
self.t.start()
func()
self.t = threading.Timer(sec, func_wrapper)
self.t.start()
def cancel(self):
self.t.cancel()
Certaines des réponses ci-dessus qui utilisent func_wrapper
et threading.Timer
fonctionnent, sauf qu’elles génèrent un nouveau thread chaque fois qu’un intervalle est appelé, ce qui cause des problèmes de mémoire.
L'exemple de base ci-dessous à peu près / a implémenté un mécanisme similaire en plaçant interval sur un thread séparé. Il dort à l'intervalle donné. Avant de passer au code, voici certaines des limitations que vous devez connaître:
JavaScript est un seul thread, donc lorsque la fonction dans setInterval
est activée, rien d’autre ne fonctionnera en même temps (sauf le thread de travail, mais parlons du cas d’utilisation générale de setInterval
. Par conséquent, le thread est sécurisé. Mais ici, dans cette implémentation, peut rencontrer des conditions de course à moins d'utiliser un threading.rLock
.
L’implémentation ci-dessous utilise time.sleep
pour simuler des intervalles, mais en ajoutant le temps d’exécution de func
, le temps total pour cet interval peut être supérieur à ce que vous attendez. Donc, selon les cas d'utilisation, vous voudrez peut-être "dormir moins" (moins le temps pris pour appeler func
)
Je n’ai fait que à peu près testé cela, et vous ne devriez certainement pas utiliser les variables globales comme je l’ai fait, n'hésitez pas à le modifier afin qu’il s’intègre dans votre système.
Assez parlé, voici le code:
# Python 2.7
import threading
import time
class Interval(object):
def __init__(self):
self.daemon_alive = True
self.thread = None # keep a reference to the thread so that we can "join"
def ticktock(self, interval, func):
while self.daemon_alive:
time.sleep(interval)
func()
num = 0
def print_num():
global num
num += 1
print 'num + 1 = ', num
def print_negative_num():
global num
print '-num = ', num * -1
intervals = {} # keep track of intervals
g_id_counter = 0 # roughly generate ids for intervals
def set_interval(interval, func):
global g_id_counter
interval_obj = Interval()
# Put this interval on a new thread
t = threading.Thread(target=interval_obj.ticktock, args=(interval, func))
t.setDaemon(True)
interval_obj.thread = t
t.start()
# Register this interval so that we can clear it later
# using roughly generated id
interval_id = g_id_counter
g_id_counter += 1
intervals[interval_id] = interval_obj
# return interval id like it does in JavaScript
return interval_id
def clear_interval(interval_id):
# terminate this interval's while loop
intervals[interval_id].daemon_alive = False
# kill the thread
intervals[interval_id].thread.join()
# pop out the interval from registry for reusing
intervals.pop(interval_id)
if __== '__main__':
num_interval = set_interval(1, print_num)
neg_interval = set_interval(3, print_negative_num)
time.sleep(10) # Sleep 10 seconds on main thread to let interval run
clear_interval(num_interval)
clear_interval(neg_interval)
print "- Are intervals all cleared?"
time.sleep(3) # check if both intervals are stopped (not printing)
print "- Yup, time to get beers"
Production attendue:
num + 1 = 1
num + 1 = 2
-num = -2
num + 1 = 3
num + 1 = 4
num + 1 = 5
-num = -5
num + 1 = 6
num + 1 = 7
num + 1 = 8
-num = -8
num + 1 = 9
num + 1 = 10
-num = -10
Are intervals all cleared?
Yup, time to get beers
Voici une solution à faible dérive temporelle qui utilise un thread pour signaler périodiquement un objet Event. Le thread du run () ne fait presque rien en attendant un délai d'attente; d'où la faible dérive temporelle.
# Example of low drift (time) periodic execution of a function.
import threading
import time
# Thread that sets 'flag' after 'timeout'
class timerThread (threading.Thread):
def __init__(self , timeout , flag):
threading.Thread.__init__(self)
self.timeout = timeout
self.stopFlag = False
self.event = threading.Event()
self.flag = flag
# Low drift run(); there is only the 'if'
# and 'set' methods between waits.
def run(self):
while not self.event.wait(self.timeout):
if self.stopFlag:
break
self.flag.set()
def stop(self):
stopFlag = True
self.event.set()
# Data.
printCnt = 0
# Flag to print.
printFlag = threading.Event()
# Create and start the timer thread.
printThread = timerThread(3 , printFlag)
printThread.start()
# Loop to wait for flag and print time.
while True:
global printCnt
# Wait for flag.
printFlag.wait()
# Flag must be manually cleared.
printFlag.clear()
print(time.time())
printCnt += 1
if printCnt == 3:
break;
# Stop the thread and exit.
printThread.stop()
printThread.join()
print('Done')