पायथन थ्रेडिंग।टाइमर - प्रत्येक 'एन' सेकंड में फ़ंक्शन दोहराएं


94

मैं हर 0.5 सेकंड में एक फंक्शन बंद करना चाहता हूं और टाइमर को शुरू करने और रोकने और रीसेट करने में सक्षम होना चाहिए। मैं बहुत जानकार नहीं हूँ कि पायथन धागे कैसे काम करते हैं और अजगर टाइमर के साथ कठिनाइयों का सामना कर रहे हैं।

हालांकि, मैं RuntimeError: threads can only be started onceतब मिलता रहता हूं जब मैं threading.timer.start()दो बार निष्पादित करता हूं । क्या इसके आसपास कोई कार्य है? मैंने threading.timer.cancel()प्रत्येक शुरुआत से पहले आवेदन करने की कोशिश की ।

छद्म कोड:

t=threading.timer(0.5,function)
while True:
    t.cancel()
    t.start()

जवाबों:


112

सबसे अच्छा तरीका यह है कि एक बार टाइमर थ्रेड शुरू करें। अपने टाइमर थ्रेड के अंदर आप निम्नलिखित कोड डालेंगे

class MyThread(Thread):
    def __init__(self, event):
        Thread.__init__(self)
        self.stopped = event

    def run(self):
        while not self.stopped.wait(0.5):
            print("my thread")
            # call a function

टाइमर शुरू करने वाले कोड में, आप टाइमर setको रोकने के लिए घटना को रोक सकते हैं।

stopFlag = Event()
thread = MyThread(stopFlag)
thread.start()
# this will stop the timer
stopFlag.set()

4
फिर यह खत्म हो जाएगा यह नींद है और बाद में बंद हो जाएगा। अजगर में एक धागा को जबरन निलंबित करने का कोई तरीका नहीं है । यह अजगर डेवलपर्स द्वारा किया गया एक डिज़ाइन निर्णय है। हालाँकि नेट का परिणाम समान होगा। आप थ्रेड थोड़ी देर के लिए (नींद) चलाएंगे, लेकिन यह आपके कार्य को नहीं करेगा।
— हंस फिर

13
ठीक है, वास्तव में, यदि आप टाइमर थ्रेड को तुरंत बंद करने में सक्षम होना चाहते हैं, तो बस इसके बजाय threading.Eventऔर का उपयोग करें । फिर, इसे जगाने के लिए, बस घटना को सेट करें। आपको उस समय की भी आवश्यकता नहीं है क्योंकि आप केवल ईवेंट ध्वज की जांच करते हैं। waitsleepself.stopped
— nnonneo

3
टाइमर थ्रेड को बाधित करने के लिए घटना का कड़ाई से उपयोग किया जाएगा। आम तौर पर, event.waitकेवल टाइमआउट होता है और नींद की तरह कार्य करता है, लेकिन यदि आप थ्रेड को रोकना चाहते हैं (या अन्यथा थ्रेड को रोकते हैं) तो आप थ्रेड की घटना को सेट करेंगे और यह तुरंत जाग जाएगा।
— nnonneo

2
मैंने Event.wait () का उपयोग करने के लिए अपना उत्तर अपडेट कर दिया है। सुझाव के लिए धन्यवाद।
— हंस फिर

1
बस एक सवाल है, मैं उसके बाद धागे को कैसे पुनः आरंभ कर सकता हूं? कॉलिंग thread.start()मुझे देता हैthreads can only be started once
— मोटकेम एमके

33

से अजगर में setInterval के समतुल्य :

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

उपयोग:

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

या यहाँ एक ही कार्यक्षमता है लेकिन एक डेकोरेटर के बजाय एक स्टैंडअलोन फ़ंक्शन के रूप में :

cancel_future_calls = call_repeatedly(60, print, "Hello, World")
# ...
cancel_future_calls() 

यहां बताया गया है कि थ्रेड्स का उपयोग किए बिना इसे कैसे करें ।


डेकोरेटर का उपयोग करते समय आप अंतराल को कैसे बदलेंगे? मैं बदलना चाहता हूँ .5s रनटाइम में 1 सेकंड या जो भी हो?
— lightxx

@ प्रकाश: बस का उपयोग करें @setInterval(1)।
— jfs

एचएम। इसलिए या तो मैं थोड़ा धीमा हूं या आपने मुझे गलत समझा। मेरा मतलब रनटाइम पर था। मुझे पता है कि मैं किसी भी समय स्रोत कोड में डेकोरेटर को बदल सकता हूं। क्या, उदाहरण के लिए, मेरे पास तीन कार्य थे, प्रत्येक को एक @setInterval (n) से सजाया गया था। अब रनटाइम में मैं फंक्शन 2 के अंतराल को बदलना चाहता हूं, लेकिन फ़ंक्शन 1 और 3 को अकेला छोड़ देता हूं।
— lightxx

@ प्रकाश: आप विभिन्न इंटरफ़ेस का उपयोग कर सकते हैं, जैसे stop = repeat(every=second, call=your_function); ...; stop()।
— JFS

1
@lightxx: यहाँ एक गैर डेकोरेटर है stop = call_repeatedly(interval, your_function); ...; stop()कार्यान्वयन
— JFS

31

टाइमर थ्रेड का उपयोग करना-

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

इससे रोका जा सकता है t.cancel()


3
मेरा मानना ​​है कि इस कोड की cancelविधि में एक बग है । जब यह कहा जाता है, तो धागा 1) या तो नहीं चल रहा है या 2) चल रहा है। 1 में) हम फ़ंक्शन को चलाने के लिए इंतजार कर रहे हैं, इसलिए रद्द करना ठीक काम करेगा। 2) हम वर्तमान में चल रहे हैं, इसलिए रद्द करने से वर्तमान निष्पादन पर कोई प्रभाव नहीं पड़ेगा। इसके अलावा, मौजूदा निष्पादन में ही फेरबदल होता है, इसलिए भविष्य के एटिहार में इसका प्रभाव नहीं होगा।
— रिच एपिस्कॉपो

1
यह कोड टाइमर के रन डाउन होने पर हर बार एक नया थ्रेड बनाता है। स्वीकृत उत्तर की तुलना में यह एक विशाल कचरा है।
— एड्रियन डब्ल्यू

इस समाधान से बचा जाना चाहिए, ऊपर वर्णित कारण के लिए: यह हर बार एक नया धागा बनाता है
— पिंचिया

17

फिर हंस के उत्तर पर थोड़ा सुधार करते हुए , हम सिर्फ टाइमर फ़ंक्शन को उपवर्गित कर सकते हैं। निम्नलिखित हमारा संपूर्ण "रिपीट टाइमर" कोड बन जाता है , और इसे थ्रेडिंग के लिए ड्रॉप-इन रिप्लेसमेंट के रूप में इस्तेमाल किया जा सकता है। सभी समान तर्कों के साथ:

from threading import Timer

class RepeatTimer(Timer):
    def run(self):
        while not self.finished.wait(self.interval):
            self.function(*self.args, **self.kwargs)

उपयोग उदाहरण:

def dummyfn(msg="foo"):
    print(msg)

timer = RepeatTimer(1, dummyfn)
timer.start()
time.sleep(5)
timer.cancel()

निम्नलिखित उत्पादन का उत्पादन:

foo
foo
foo
foo

तथा

timer = RepeatTimer(1, dummyfn, args=("bar",))
timer.start()
time.sleep(5)
timer.cancel()

पैदा करता है

bar
bar
bar
bar

क्या यह दृष्टिकोण मुझे टाइमर थ्रेड को प्रारंभ / रद्द / प्रारंभ / रद्द करने की अनुमति देगा?
— पॉल नोपफ

1
नहीं, जबकि यह दृष्टिकोण आपको एक साधारण टाइमर के साथ कुछ भी करने की अनुमति देता है, आप एक साधारण टाइमर के साथ ऐसा नहीं कर सकते। चूंकि प्रारंभ / रद्द करना अंतर्निहित थ्रेड से संबंधित है, यदि आप .start () एक थ्रेड जो पहले .cancel () 'एड का प्रयास करते हैं, तो आपको एक अपवाद मिलेगा RuntimeError: threads can only be started once।
— right2clicky

वास्तव में सुंदर समाधान! अजीब बात है कि उन्होंने सिर्फ एक वर्ग को शामिल नहीं किया है जो ऐसा करता है।
— रोजर डाहल

यह समाधान बहुत प्रभावशाली है, लेकिन मैं यह समझने के लिए संघर्ष करता हूं कि इसे केवल पायथन 3 थ्रेडिंग टाइमर इंटरफ़ेस प्रलेखन को पढ़ने से कैसे डिज़ाइन किया गया था । उत्तर threading.pyमॉड्यूल में जाने से कार्यान्वयन को जानने के लिए निर्मित होता है।
— Adam.at.Epsilon

14

ओपी के अनुरोध के अनुसार टाइमर का उपयोग करके एक सही उत्तर देने के हित में, मैं स्वप्निल जरीवाला के उत्तर पर सुधार करूंगा :

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

3

मैंने थोड़ा सांत्वना घड़ी बनाने के लिए स्वप्निल-जरीवाला कोड में कुछ कोड को बदल दिया है।

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
...

टिंकर ग्राफिक इंटरफ़ेस के साथ टाइमर

यह कोड घड़ी के टाइमर को टिंकर के साथ थोड़ी सी खिड़की में रखता है

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

फ्लैशकार्ड गेम का एक उदाहरण (सॉर्ट)

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

उत्पादन:

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

यदि hFunction अवरुद्ध है, तो इससे बाद के शुरुआती समय में कुछ देरी नहीं होगी? हो सकता है कि आप लाइनों को राउंड स्वैप कर सकें ताकि हैंडल_फंक्शन पहले टाइमर शुरू हो और फिर कॉल करें hFunction?
— मूंछें

2

मुझे एक प्रोजेक्ट के लिए ऐसा करना पड़ा। मैंने जो किया, वह कार्य के लिए एक अलग सूत्र शुरू कर रहा था

t = threading.Thread(target =heartbeat, args=(worker,))
t.start()

**** दिल की धड़कन मेरा कार्य है, कार्यकर्ता मेरे तर्कों में से एक है ****

मेरे दिल की धड़कन समारोह के अंदर:

def heartbeat(worker):

    while True:
        time.sleep(5)
        #all of my code

इसलिए जब मैं थ्रेड शुरू करता हूं तो फ़ंक्शन बार-बार 5 सेकंड इंतजार करेगा, मेरे सभी कोड चलाएगा, और वह अनिश्चित काल तक करेगा। यदि आप प्रक्रिया को मारना चाहते हैं तो बस धागे को मारें।


1

मैंने एक वर्ग लागू किया है जो टाइमर के रूप में काम करता है।

अगर किसी को इसकी ज़रूरत हो तो मैं यहाँ लिंक छोड़ देता हूँ: https://github.com/ivanhalencp/python/tree/master/xTimer


3
जब भी यह सैद्धांतिक रूप से प्रश्न का उत्तर दे सकता है, तो यहां उत्तर के आवश्यक भागों को शामिल करना और संदर्भ के लिए लिंक प्रदान करना बेहतर होगा ।
— कार्ल रिक्टर

1
from threading import Timer
def TaskManager():
    #do stuff
    t = Timer( 1, TaskManager )
    t.start()

TaskManager()

यहां छोटा सा नमूना है, यह दांव को समझने में मदद करेगा कि यह कैसे चलता है। फ़ंक्शन taskManager () अंत में विलंबित फ़ंक्शन कॉल को स्वयं बनाते हैं।

"डलाय" चर को बदलने की कोशिश करें और आप अंतर देख पाएंगे

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

1
क्या आप यह भी बता सकते हैं कि यह काम क्यों करता है?
— मिनोचा

आपका उदाहरण थोड़ा भ्रमित करने वाला था, पहला कोडब्लॉक जो आपको कहना चाहिए था।
— भाग

1

मुझे right2clicky का जवाब पसंद है, विशेष रूप से उस में इसे फाड़ने के लिए थ्रेड की आवश्यकता नहीं होती है और हर बार टाइमर टिक करने पर एक नया बनाया जाता है। इसके अतिरिक्त, टाइमर कॉलबैक के साथ एक क्लास बनाना एक आसान ओवरराइड है जिसे समय-समय पर कॉल किया जाता है। यह मेरा सामान्य उपयोग मामला है:

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

1

यह क्लास के बजाय फ़ंक्शन का उपयोग करके एक वैकल्पिक कार्यान्वयन है। ऊपर @Andrew Wilkins से प्रेरित।

क्योंकि प्रतीक्षा नींद से अधिक सटीक है (यह फ़ंक्शन रनटाइम को ध्यान में रखता है):

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

1

मैं सिंगलटॉन वर्ग के साथ एक और समाधान के साथ आया हूं। कृपया मुझे बताएं कि क्या कोई स्मृति रिसाव यहाँ है।

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()
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.