Chcę uruchamiać funkcję co 0,5 sekundy i mieć możliwość uruchamiania i zatrzymywania oraz resetowania licznika czasu. Nie mam zbyt dużej wiedzy na temat działania wątków Pythona i mam problemy z zegarem Pythona.
Jednak wciąż otrzymuję, RuntimeError: threads can only be started once
gdy wykonuję threading.timer.start()
dwa razy. Czy jest na to jakiś sposób? Próbowałem aplikować threading.timer.cancel()
przed każdym startem.
Pseudo kod:
t=threading.timer(0.5,function)
while True:
t.cancel()
t.start()
python
python-3.x
python-2.7
user1431282
źródło
źródło
threading.Event
iwait
zamiastsleep
. Następnie, aby go obudzić, wystarczy ustawić wydarzenie. Nie potrzebujesz nawetself.stopped
wtedy, ponieważ po prostu sprawdzasz flagę wydarzenia.event.wait
prostu wygaśnie i zachowa się jak sen, ale jeśli chcesz zatrzymać (lub w inny sposób przerwać wątek), ustawisz zdarzenie wątku i natychmiast się obudzisz.thread.start()
daje mithreads can only be started once
Z odpowiednika setInterval w Pythonie :
import threading def setInterval(interval): def decorator(function): def wrapper(*args, **kwargs): stopped = threading.Event() def loop(): # executed in another thread while not stopped.wait(interval): # until stopped function(*args, **kwargs) t = threading.Thread(target=loop) t.daemon = True # stop if the program exits t.start() return stopped return wrapper return decorator
Stosowanie:
@setInterval(.5) def function(): "..." stop = function() # start timer, the first call is in .5 seconds stop.set() # stop the loop stop = function() # start new timer # ... stop.set()
Lub tutaj jest ta sama funkcjonalność, ale jako samodzielna funkcja zamiast dekoratora :
cancel_future_calls = call_repeatedly(60, print, "Hello, World") # ... cancel_future_calls()
Oto jak to zrobić bez używania wątków .
źródło
@setInterval(1)
.stop = repeat(every=second, call=your_function); ...; stop()
.stop = call_repeatedly(interval, your_function); ...; stop()
implementacjaKorzystanie z wątków czasowych
from threading import Timer,Thread,Event class perpetualTimer(): def __init__(self,t,hFunction): self.t=t self.hFunction = hFunction self.thread = Timer(self.t,self.handle_function) def handle_function(self): self.hFunction() self.thread = Timer(self.t,self.handle_function) self.thread.start() def start(self): self.thread.start() def cancel(self): self.thread.cancel() def printer(): print 'ipsem lorem' t = perpetualTimer(5,printer) t.start()
można to zatrzymać
t.cancel()
źródło
cancel
metodzie. Po wywołaniu wątek 1) nie działa lub 2) działa. W 1) czekamy na uruchomienie funkcji, więc anulowanie będzie działać poprawnie. w 2), w którym aktualnie pracujemy, więc anulowanie nie będzie miało wpływu na bieżące wykonanie. co więcej, bieżące wykonanie zmienia harmonogram, więc nie będzie miało wpływu na przyszłe etihery.Trochę poprawiając odpowiedź Hansa Then , możemy po prostu podklasować funkcję Timer. Poniższy kod staje się naszym całym kodem „licznika powtórzeń” i może być używany jako zamiennik typu drop-in dla wątków.Timer z tymi samymi argumentami:
from threading import Timer class RepeatTimer(Timer): def run(self): while not self.finished.wait(self.interval): self.function(*self.args, **self.kwargs)
Przykład użycia:
def dummyfn(msg="foo"): print(msg) timer = RepeatTimer(1, dummyfn) timer.start() time.sleep(5) timer.cancel()
generuje następujący wynik:
i
timer = RepeatTimer(1, dummyfn, args=("bar",)) timer.start() time.sleep(5) timer.cancel()
produkuje
źródło
RuntimeError: threads can only be started once
.threading.py
modułu.Aby udzielić poprawnej odpowiedzi przy użyciu Timera zgodnie z żądaniem OP, poprawię odpowiedź swapnil jariwala :
from threading import Timer class InfiniteTimer(): """A Timer class that does not stop, unless you want it to.""" def __init__(self, seconds, target): self._should_continue = False self.is_running = False self.seconds = seconds self.target = target self.thread = None def _handle_target(self): self.is_running = True self.target() self.is_running = False self._start_timer() def _start_timer(self): if self._should_continue: # Code could have been running when cancel was called. self.thread = Timer(self.seconds, self._handle_target) self.thread.start() def start(self): if not self._should_continue and not self.is_running: self._should_continue = True self._start_timer() else: print("Timer already started or running, please wait if you're restarting.") def cancel(self): if self.thread is not None: self._should_continue = False # Just in case thread is running and cancel fails. self.thread.cancel() else: print("Timer never started or failed to initialize.") def tick(): print('ipsem lorem') # Example Usage t = InfiniteTimer(0.5, tick) t.start()
źródło
Zmieniłem trochę kodu w kodzie swapnil-jariwala, aby stworzyć mały zegar konsoli.
from threading import Timer, Thread, Event from datetime import datetime class PT(): def __init__(self, t, hFunction): self.t = t self.hFunction = hFunction self.thread = Timer(self.t, self.handle_function) def handle_function(self): self.hFunction() self.thread = Timer(self.t, self.handle_function) self.thread.start() def start(self): self.thread.start() def printer(): tempo = datetime.today() h,m,s = tempo.hour, tempo.minute, tempo.second print(f"{h}:{m}:{s}") t = PT(1, printer) t.start()
>>> 11:39:11 11:39:12 11:39:13 11:39:14 11:39:15 11:39:16 ...
Zegar z graficznym interfejsem tkinter
Ten kod umieszcza licznik czasu w małym okienku z tkinter
from threading import Timer, Thread, Event from datetime import datetime import tkinter as tk app = tk.Tk() lab = tk.Label(app, text="Timer will start in a sec") lab.pack() class perpetualTimer(): def __init__(self, t, hFunction): self.t = t self.hFunction = hFunction self.thread = Timer(self.t, self.handle_function) def handle_function(self): self.hFunction() self.thread = Timer(self.t, self.handle_function) self.thread.start() def start(self): self.thread.start() def cancel(self): self.thread.cancel() def printer(): tempo = datetime.today() clock = "{}:{}:{}".format(tempo.hour, tempo.minute, tempo.second) try: lab['text'] = clock except RuntimeError: exit() t = perpetualTimer(1, printer) t.start() app.mainloop()
Przykład gry w karty flash (w pewnym sensie)
from threading import Timer, Thread, Event from datetime import datetime class perpetualTimer(): def __init__(self, t, hFunction): self.t = t self.hFunction = hFunction self.thread = Timer(self.t, self.handle_function) def handle_function(self): self.hFunction() self.thread = Timer(self.t, self.handle_function) self.thread.start() def start(self): self.thread.start() def cancel(self): self.thread.cancel() x = datetime.today() start = x.second def printer(): global questions, counter, start x = datetime.today() tempo = x.second if tempo - 3 > start: show_ans() #print("\n{}:{}:{}".format(tempo.hour, tempo.minute, tempo.second), end="") print() print("-" + questions[counter]) counter += 1 if counter == len(answers): counter = 0 def show_ans(): global answers, c2 print("It is {}".format(answers[c2])) c2 += 1 if c2 == len(answers): c2 = 0 questions = ["What is the capital of Italy?", "What is the capital of France?", "What is the capital of England?", "What is the capital of Spain?"] answers = "Rome", "Paris", "London", "Madrid" counter = 0 c2 = 0 print("Get ready to answer") t = perpetualTimer(3, printer) t.start()
wynik:
Get ready to answer >>> -What is the capital of Italy? It is Rome -What is the capital of France? It is Paris -What is the capital of England? ...
źródło
Musiałem to zrobić dla projektu. Skończyło się na tym, że uruchomiłem osobny wątek dla funkcji
**** bicie serca to moja funkcja, pracownik to jeden z moich argumentów ****
wewnątrz funkcji bicia serca:
def heartbeat(worker): while True: time.sleep(5) #all of my code
Więc kiedy uruchamiam wątek, funkcja będzie wielokrotnie czekać 5 sekund, uruchamiać cały mój kod i robić to w nieskończoność. Jeśli chcesz zabić proces, po prostu zabij wątek.
źródło
Zaimplementowałem klasę, która działa jako licznik czasu.
Zostawiam link tutaj na wypadek, gdyby ktoś go potrzebował: https://github.com/ivanhalencp/python/tree/master/xTimer
źródło
from threading import Timer def TaskManager(): #do stuff t = Timer( 1, TaskManager ) t.start() TaskManager()
Oto mała próbka, która pomoże lepiej zrozumieć, jak to działa. function taskManager () na końcu tworzy opóźnione wywołanie funkcji do siebie.
Spróbuj zmienić zmienną „dalay”, a zobaczysz różnicę
from threading import Timer, _sleep # ------------------------------------------ DATA = [] dalay = 0.25 # sec counter = 0 allow_run = True FIFO = True def taskManager(): global counter, DATA, delay, allow_run counter += 1 if len(DATA) > 0: if FIFO: print("["+str(counter)+"] new data: ["+str(DATA.pop(0))+"]") else: print("["+str(counter)+"] new data: ["+str(DATA.pop())+"]") else: print("["+str(counter)+"] no data") if allow_run: #delayed method/function call to it self t = Timer( dalay, taskManager ) t.start() else: print(" END task-manager: disabled") # ------------------------------------------ def main(): DATA.append("data from main(): 0") _sleep(2) DATA.append("data from main(): 1") _sleep(2) # ------------------------------------------ print(" START task-manager:") taskManager() _sleep(2) DATA.append("first data") _sleep(2) DATA.append("second data") print(" START main():") main() print(" END main():") _sleep(2) DATA.append("last data") allow_run = False
źródło
Podoba mi się odpowiedź right2clicky, zwłaszcza, że nie wymaga ona zrywania wątku i tworzenia nowego za każdym razem, gdy Timer tyka. Ponadto łatwo jest utworzyć klasę z wywołaniem zwrotnym timera, które jest wywoływane okresowo. To mój normalny przypadek użycia:
class MyClass(RepeatTimer): def __init__(self, period): super().__init__(period, self.on_timer) def on_timer(self): print("Tick") if __name__ == "__main__": mc = MyClass(1) mc.start() time.sleep(5) mc.cancel()
źródło
Jest to alternatywna implementacja wykorzystująca funkcję zamiast klasy. Zainspirowany przez @Andrew Wilkins powyżej.
Ponieważ czekanie jest dokładniejsze niż uśpienie (bierze pod uwagę czas działania funkcji):
import threading PING_ON = threading.Event() def ping(): while not PING_ON.wait(1): print("my thread %s" % str(threading.current_thread().ident)) t = threading.Thread(target=ping) t.start() sleep(5) PING_ON.set()
źródło
Wymyśliłem inne rozwiązanie z klasą SingleTon. Proszę, powiedz mi, czy doszło do wycieku pamięci.
import time,threading class Singleton: __instance = None sleepTime = 1 executeThread = False def __init__(self): if Singleton.__instance != None: raise Exception("This class is a singleton!") else: Singleton.__instance = self @staticmethod def getInstance(): if Singleton.__instance == None: Singleton() return Singleton.__instance def startThread(self): self.executeThread = True self.threadNew = threading.Thread(target=self.foo_target) self.threadNew.start() print('doing other things...') def stopThread(self): print("Killing Thread ") self.executeThread = False self.threadNew.join() print(self.threadNew) def foo(self): print("Hello in " + str(self.sleepTime) + " seconds") def foo_target(self): while self.executeThread: self.foo() print(self.threadNew) time.sleep(self.sleepTime) if not self.executeThread: break sClass = Singleton() sClass.startThread() time.sleep(5) sClass.getInstance().stopThread() sClass.getInstance().sleepTime = 2 sClass.startThread()
źródło
Oprócz powyższych świetnych odpowiedzi przy użyciu wątków, na wypadek, gdybyś musiał użyć swojego głównego wątku lub wolisz podejście asynchroniczne - owinąłem krótką klasę wokół klasy aio_timers Timer (aby umożliwić powtarzanie)
import asyncio from aio_timers import Timer class RepeatingAsyncTimer(): def __init__(self, interval, cb, *args, **kwargs): self.interval = interval self.cb = cb self.args = args self.kwargs = kwargs self.aio_timer = None self.start_timer() def start_timer(self): self.aio_timer = Timer(delay=self.interval, callback=self.cb_wrapper, callback_args=self.args, callback_kwargs=self.kwargs ) def cb_wrapper(self, *args, **kwargs): self.cb(*args, **kwargs) self.start_timer() from time import time def cb(timer_name): print(timer_name, time()) print(f'clock starts at: {time()}') timer_1 = RepeatingAsyncTimer(interval=5, cb=cb, timer_name='timer_1') timer_2 = RepeatingAsyncTimer(interval=10, cb=cb, timer_name='timer_2')
zegar zaczyna się od: 16024388 40 .9690785
timer_ 1 16024388 45 .980087
timer_ 2 16024388 50 .9806316
timer_ 1 16024388 50 .9808934
timer_ 1 16.024.388 55 +0,9863033
timer_ 2 16.024.388 60 0,9868324
timer_ 1 16.024.388 60 ,9876585
źródło