วิธีที่ดีที่สุดในการใช้งานฟังก์ชั่นซ้ำ ๆ ทุก ๆ วินาทีคืออะไร?


283

ฉันต้องการใช้งานฟังก์ชั่นซ้ำ ๆ ใน Python ทุก ๆ 60 วินาทีตลอดไป (เช่นเดียวกับNSTimerใน Objective C) รหัสนี้จะทำงานเป็น daemon และมีประสิทธิภาพเหมือนกับการเรียกสคริปต์ python ทุก ๆ นาทีโดยใช้ cron แต่ไม่ต้องการให้ผู้ใช้ตั้งค่า

ในคำถามนี้เกี่ยวกับ cron ที่ใช้ใน Pythonวิธีแก้ปัญหาดูเหมือนจะมีประสิทธิภาพเพียงแค่sleep ()เป็นเวลา x วินาที ฉันไม่ต้องการฟังก์ชั่นขั้นสูงดังกล่าวดังนั้นบางทีสิ่งนี้จะทำงาน

while True:
    # Code executed here
    time.sleep(60)

มีปัญหาใด ๆ ที่มองเห็นได้ในรหัสนี้หรือไม่?


83
จุด pedantic แต่อาจสำคัญรหัสของคุณด้านบนรหัสไม่ได้ดำเนินการทุก ๆ 60 วินาทีมันทำให้ช่องว่าง 60 วินาทีระหว่างการประหารชีวิต มันจะเกิดขึ้นทุก ๆ 60 วินาทีหากรหัสที่คุณเรียกใช้นั้นไม่มีเวลาเลย
Simon

4
ก็time.sleep(60)อาจกลับมาทั้งก่อนหน้าและภายหลัง
jfs

5
ฉันยังคงสงสัยว่า: มีปัญหาใด ๆ ที่มองเห็นได้ในรหัสนี้หรือไม่?
Banana

1
"ปัญหาที่คาดการณ์ได้" คือคุณไม่สามารถคาดหวังได้ 60 ครั้งต่อชั่วโมงเพียงแค่ใช้ time.sleep (60) ดังนั้นหากคุณต่อท้ายหนึ่งรายการต่อการวนซ้ำและเก็บรายการความยาวที่ตั้งไว้ ... ค่าเฉลี่ยของรายการนั้นจะไม่แสดง "ระยะเวลา" ที่สอดคล้องกันของเวลา ดังนั้นฟังก์ชั่นเช่น "ค่าเฉลี่ยเคลื่อนที่" สามารถอ้างอิงจุดข้อมูลที่เก่าเกินไปซึ่งจะบิดเบือนการบ่งชี้ของคุณ
litepresence

2
@ บานาน่าใช่คุณสามารถคาดหวังว่าปัญหาใด ๆ ที่เกิดขึ้นเพราะสคริปต์ของคุณจะไม่ถูกเรียกใช้งานทุก ๆ 60 วินาที ตัวอย่างเช่น ฉันเริ่มทำบางสิ่งเช่นนี้เพื่อแบ่งสตรีมวิดีโอและ upload'em และท้ายที่สุดฉันก็เริ่มมีความยาวขึ้น 5-10 ~ วินาทีเพราะคิวสื่อบัฟเฟอร์ในขณะที่ประมวลผลข้อมูลภายในลูป ขึ้นอยู่กับข้อมูลของคุณ หากฟังก์ชั่นเป็นสุนัขเฝ้าบ้านแบบง่าย ๆ ที่เตือนคุณเช่นเมื่อดิสก์ของคุณเต็มคุณควรไม่มีปัญหาเลยถ้าคุณตรวจสอบการแจ้งเตือนโรงไฟฟ้านิวเคลียร์คุณอาจพบกับเมือง ปลิวสมบูรณ์ x
DGoiko

คำตอบ:


229

ถ้าโปรแกรมของคุณไม่ได้ห่วงเหตุการณ์แล้วใช้schedโมดูลซึ่งการดำเนินการวัตถุประสงค์ทั่วไปจัดตารางเวลาเหตุการณ์

import sched, time
s = sched.scheduler(time.time, time.sleep)
def do_something(sc): 
    print("Doing stuff...")
    # do your stuff
    s.enter(60, 1, do_something, (sc,))

s.enter(60, 1, do_something, (s,))
s.run()

หากคุณกำลังใช้ห้องสมุดห่วงเหตุการณ์เช่นasyncio, trio, tkinter, PyQt5, gobject, kivyและอื่น ๆ อีกมากมาย - เพียงแค่กำหนดการงานโดยใช้วิธีการที่มีอยู่เหตุการณ์ห่วงห้องสมุดของคุณแทน


16
โมดูลกำหนดเวลาสำหรับฟังก์ชั่นการตั้งเวลาให้ทำงานหลังจากผ่านไประยะหนึ่งคุณจะใช้มันเพื่อเรียกฟังก์ชั่นซ้ำทุก ๆ x วินาทีโดยไม่ใช้ time.sleep () ได้อย่างไร?
Baishampayan Ghose

2
@ Baishampayan: เพียงกำหนดการวิ่งใหม่
nosklo

3
จากนั้น apscheduler ที่packages.python.org/APSchedulerควรได้รับการกล่าวถึง ณ จุดนี้
Daniel F

6
หมายเหตุ: รุ่นนี้อาจลอย คุณสามารถใช้enterabs()เพื่อหลีกเลี่ยงมัน ต่อไปนี้เป็นรุ่นที่ไม่ใช่ลอยสำหรับการเปรียบเทียบ
jfs

8
@ JavaSa: เพราะ"ทำสิ่งที่คุณ"ไม่ได้เกิดขึ้นทันทีและข้อผิดพลาดจากtime.sleepอาจสะสมที่นี่ "ดำเนินการทุก ๆ X วินาที" และ "ดำเนินการด้วยความล่าช้า ~ X วินาทีซ้ำ ๆ " ไม่เหมือนกัน ดู ความคิดเห็นนี้ด้วย
jfs

180

ล็อคเวลาของคุณกับนาฬิกาของระบบดังนี้:

import time
starttime = time.time()
while True:
    print "tick"
    time.sleep(60.0 - ((time.time() - starttime) % 60.0))

22
+1 คุณและtwistedคำตอบเป็นคำตอบเดียวที่เรียกใช้ฟังก์ชั่นทุกxวินาที ส่วนที่เหลือจะดำเนินการฟังก์ชั่นด้วยความล่าช้าเป็นxวินาทีหลังจากการโทรแต่ละครั้ง
jfs

13
หากคุณต้องการเพิ่มรหัสบางอย่างลงในนี้ซึ่งใช้เวลานานกว่าหนึ่งวินาที ... มันจะทิ้งเวลาและเริ่มล้าหลัง .. คำตอบที่ยอมรับในกรณีนี้ถูกต้อง ... ทุกคนสามารถวนรอบคำสั่งพิมพ์ง่าย และมีมันทำงานทุกวินาทีโดยไม่ชักช้า ...
โกรธ 84

5
ฉันชอบfrom time import time, sleepเพราะความหมายที่มีอยู่;)
จะ

14
ทำงานได้อย่างน่าอัศจรรย์ ไม่จำเป็นต้องลบของคุณstarttimeถ้าคุณเริ่มต้นด้วยการซิงค์กับเวลาที่กำหนด: time.sleep(60 - time.time() % 60)ทำงานได้ดีสำหรับฉัน ฉันใช้มันtime.sleep(1200 - time.time() % 1200)และทำให้ฉันมีบันทึกใน:00 :20 :40ตรงตามที่ฉันต้องการ
TemporalWolf

2
@AntonSchigur เพื่อหลีกเลี่ยงการล่องลอยหลังจากทำซ้ำหลาย ๆ ซ้ำแต่ละคนอาจเริ่มต้นเร็วหรือช้าเล็กน้อยขึ้นอยู่กับsleep(), timer()ความแม่นยำและใช้เวลานานเท่าใดในการดำเนินการของร่างกายห่วง แต่ในการทำซ้ำโดยเฉลี่ยมักจะเกิดขึ้นในช่วงขอบเขต while keep_doing_it(): sleep(interval - timer() % interval)(แม้ว่าบางส่วนจะถูกข้าม): เปรียบเทียบกับwhile keep_doing_it(): sleep(interval)ข้อผิดพลาดที่อาจสะสมหลังจากทำซ้ำหลายครั้ง
jfs

71

คุณอาจต้องการที่จะต้องพิจารณาบิดซึ่งเป็นเครือข่ายห้องสมุดหลามที่ใช้รูปแบบเครื่องปฏิกรณ์

from twisted.internet import task, reactor

timeout = 60.0 # Sixty seconds

def doWork():
    #do work here
    pass

l = task.LoopingCall(doWork)
l.start(timeout) # call every sixty seconds

reactor.run()

ในขณะที่ "ในขณะที่ True: sleep (60)" อาจใช้งานได้ Twisted อาจใช้คุณลักษณะหลายอย่างที่คุณต้องการในที่สุด (การจัดการการบันทึกการทำงานหรือการจัดการข้อยกเว้นตามที่ระบุไว้โดย Bobince) และอาจเป็นโซลูชันที่แข็งแกร่งกว่า


คำตอบที่ยอดเยี่ยมเช่นกันแม่นยำมากโดยไม่ต้องเลื่อน ฉันสงสัยว่าสิ่งนี้ทำให้ซีพียูเข้าสู่โหมดสลีเช่นกันในขณะที่รอดำเนินการงาน (หรือที่เรียกว่าไม่ยุ่งรอ)
smoothware

1
สิ่งนี้ลอยอยู่ในระดับมิลลิวินาที
Derek Eden

"การดริฟท์ที่ระดับมิลลิวินาที" หมายความว่าอย่างไร
Jean-Paul Calderone

67

หากคุณต้องการวิธีที่ไม่ปิดกั้นในการใช้งานฟังก์ชั่นของคุณเป็นระยะแทนที่จะเป็นลูปที่ไม่มีขีด จำกัด ในการบล็อกฉันจะใช้ตัวจับเวลาเธรด วิธีนี้โค้ดของคุณสามารถทำงานต่อไปและทำงานอื่น ๆ และยังมีฟังก์ชั่นของคุณเรียกว่าทุก ๆ n วินาที ฉันใช้เทคนิคนี้เป็นอย่างมากสำหรับการพิมพ์ข้อมูลความคืบหน้าเกี่ยวกับงานที่ต้องใช้ CPU / ดิสก์ / เครือข่ายที่ยาวนาน

นี่คือรหัสที่ฉันโพสต์ในคำถามที่คล้ายกันกับการควบคุมเริ่มต้น () และหยุด ():

from threading import Timer

class RepeatedTimer(object):
    def __init__(self, interval, function, *args, **kwargs):
        self._timer     = None
        self.interval   = interval
        self.function   = function
        self.args       = args
        self.kwargs     = kwargs
        self.is_running = False
        self.start()

    def _run(self):
        self.is_running = False
        self.start()
        self.function(*self.args, **self.kwargs)

    def start(self):
        if not self.is_running:
            self._timer = Timer(self.interval, self._run)
            self._timer.start()
            self.is_running = True

    def stop(self):
        self._timer.cancel()
        self.is_running = False

การใช้งาน:

from time import sleep

def hello(name):
    print "Hello %s!" % name

print "starting..."
rt = RepeatedTimer(1, hello, "World") # it auto-starts, no need of rt.start()
try:
    sleep(5) # your long-running job goes here...
finally:
    rt.stop() # better in a try/finally block to make sure the program ends!

คุณสมบัติ:

  • ไลบรารีมาตรฐานเท่านั้นไม่มีการอ้างอิงภายนอก
  • start()และstop()ปลอดภัยในการโทรหลายครั้งแม้ว่าตัวจับเวลาได้เริ่ม / หยุดแล้ว
  • ฟังก์ชั่นที่จะเรียกว่าสามารถมีข้อโต้แย้งตำแหน่งและชื่อ
  • คุณสามารถเปลี่ยนแปลงได้intervalตลอดเวลาซึ่งจะมีผลหลังจากเรียกใช้ครั้งต่อไป สำหรับเดียวกันargs, kwargsและแม้กระทั่งfunction!

วิธีนี้ดูเหมือนจะลอยไปตามกาลเวลา ฉันต้องการเวอร์ชันที่มีวัตถุประสงค์เพื่อเรียกใช้ฟังก์ชันทุก ๆ n วินาทีโดยไม่เลื่อน ฉันจะโพสต์การอัปเดตในคำถามแยกต่างหาก
eraoul

ในdef _run(self)ฉันพยายามที่จะตัดหัวของฉันรอบเหตุผลที่คุณโทรหาก่อนself.start() self.function()คุณสามารถทำอย่างละเอียด? ฉันคิดว่าการโทรstart()เป็นอันดับแรกself.is_runningจะเป็นFalseเช่นนั้นเราจะปั่นด้ายใหม่เสมอ
รวย Episcopo

1
ฉันคิดว่าฉันได้ไปที่ด้านล่างของมัน วิธีการแก้ปัญหาของ @ MestreLion จะเรียกใช้ฟังก์ชั่นทุก ๆxวินาที (เช่น t = 0, t = 1x, t = 2x, t = 3x, ... ) โดยที่ตัวอย่างโค้ดต้นฉบับของโปสเตอร์จะเรียกใช้ฟังก์ชันที่มีช่วงเวลาxวินาทีในระหว่างนั้น นอกจากนี้วิธีแก้ปัญหานี้ฉันเชื่อว่ามีข้อบกพร่องหากintervalสั้นกว่าเวลาที่ใช้functionในการดำเนินการ ในกรณีนั้นself._timerจะถูกเขียนทับในstartฟังก์ชั่น
รวย Episcopo

ใช่ @RichieEpiscopo การเรียกไปยัง.function()หลังจากนั้น.start()คือการเรียกใช้ฟังก์ชันที่ t = 0 และฉันไม่คิดว่ามันจะเป็นปัญหาหากfunctionใช้เวลานานกว่าintervalแต่ใช่อาจมีเงื่อนไขการแข่งรถบางอย่างในรหัส
MestreLion

นี่เป็นวิธีเดียวที่ฉันจะได้รับ ขอบคุณ
backslashN

35

วิธีที่ง่ายกว่าที่ฉันเชื่อว่าเป็น:

import time

def executeSomething():
    #code here
    time.sleep(60)

while True:
    executeSomething()

วิธีนี้โค้ดของคุณจะถูกดำเนินการแล้วมันจะรอ 60 วินาทีจากนั้นมันจะดำเนินการอีกครั้งรอดำเนินการ ฯลฯ ... ไม่จำเป็นต้องซับซ้อนสิ่ง: D


คำหลักที่แท้จริงควรเป็นตัวพิมพ์ใหญ่
Sean Cain

38
ที่จริงนี่ไม่ใช่คำตอบ: time sleep () สามารถใช้สำหรับการรอ X วินาทีหลังจากการดำเนินการทุกครั้ง ตัวอย่างเช่นหากฟังก์ชั่นของคุณใช้เวลา 0.5 วินาทีในการดำเนินการและคุณใช้ time.sleep (1) หมายถึงฟังก์ชั่นของคุณดำเนินการทุก ๆ 1.5 วินาทีไม่ใช่ 1 คุณควรใช้โมดูลและ / หรือเธรดอื่น ๆ เพื่อให้แน่ใจว่า ในทุก ๆ X วินาที
kommradHomer

1
@kommradHomer: คำตอบของ Dave Roveแสดงให้เห็นว่าคุณสามารถใช้time.sleep()งานได้ทุก ๆ X วินาที
jfs

2
ในความคิดของฉันรหัสควรจะเรียกtime.sleep()ในwhile Trueวงที่ชอบ:def executeSomething(): print('10 sec left') ; while True: executeSomething(); time.sleep(10)
ลีโอนาร์ Lepadatu

22
import time, traceback

def every(delay, task):
  next_time = time.time() + delay
  while True:
    time.sleep(max(0, next_time - time.time()))
    try:
      task()
    except Exception:
      traceback.print_exc()
      # in production code you might want to have this instead of course:
      # logger.exception("Problem while executing repetitive task.")
    # skip tasks if we are behind schedule:
    next_time += (time.time() - next_time) // delay * delay + delay

def foo():
  print("foo", time.time())

every(5, foo)

หากคุณต้องการทำสิ่งนี้โดยไม่ปิดกั้นรหัสที่เหลือของคุณคุณสามารถใช้สิ่งนี้เพื่อให้มันทำงานในเธรดของตัวเอง:

import threading
threading.Thread(target=lambda: every(5, foo)).start()

โซลูชันนี้รวมคุณสมบัติหลายอย่างที่ไม่ค่อยพบรวมอยู่ในโซลูชันอื่น ๆ :

  • การจัดการข้อยกเว้น:เท่าที่จะทำได้ในระดับนี้ข้อยกเว้นจะได้รับการจัดการอย่างถูกต้องเช่นได้รับการบันทึกเพื่อการดีบักโดยไม่ยกเลิกโปรแกรมของเรา
  • ไม่มีการผูกมัด:การใช้งานแบบลูกโซ่ทั่วไป (สำหรับการกำหนดเวลาเหตุการณ์ถัดไป) ที่คุณพบในคำตอบมากมายนั้นมีความเปราะบางในแง่ที่ว่าหากมีสิ่งใดผิดพลาดภายในกลไกการกำหนดเวลา ( threading.Timerหรืออะไรก็ตาม) สิ่งนี้จะยุติการเชื่อมโยง จะไม่มีการประหารชีวิตอีกต่อไปแม้ว่าสาเหตุของปัญหาจะได้รับการแก้ไขแล้ว การวนซ้ำอย่างง่ายและการรอด้วยวิsleep()นั้นง่ายกว่าเมื่อเปรียบเทียบ
  • ไม่มีดริฟท์:โซลูชันของฉันคอยติดตามจำนวนครั้งที่ควรจะรัน ไม่มีการดริฟท์ขึ้นอยู่กับเวลาดำเนินการ (เช่นในโซลูชันอื่น ๆ )
  • การข้าม:โซลูชันของฉันจะข้ามงานหากการดำเนินการครั้งหนึ่งใช้เวลามากเกินไป (เช่น X ทุกห้าวินาที แต่ X ใช้เวลา 6 วินาที) นี่คือพฤติกรรม cron มาตรฐาน (และสำหรับเหตุผลที่ดี) โซลูชันอื่น ๆ อีกมากมายเพียงแค่ดำเนินการงานหลาย ๆ ครั้งติดต่อกันโดยไม่ล่าช้า สำหรับกรณีส่วนใหญ่ (เช่นงานล้างข้อมูล) สิ่งนี้ไม่ต้องการ ถ้ามันเป็นความประสงค์เพียงแค่ใช้next_time += delayแทน

2
คำตอบที่ดีที่สุดสำหรับการไม่ดริฟท์
Sebastian Stark

1
@PirateApp ฉันจะทำในหัวข้ออื่น คุณสามารถทำได้ในเธรดเดียวกัน แต่หลังจากนั้นคุณก็จบการเขียนโปรแกรมระบบการตั้งเวลาของคุณเองซึ่งซับซ้อนเกินไปสำหรับความคิดเห็น
Alfe

1
ใน Python ขอบคุณ GIL การเข้าถึงตัวแปรในสองเธรดนั้นปลอดภัยอย่างสมบูรณ์ และการอ่านในสองเธรดไม่ควรเป็นปัญหา (เช่นเดียวกันกับสภาพแวดล้อมเธรดอื่น ๆ ) การเขียนจากเธรดที่แตกต่างกันสองรายการในระบบที่ไม่มี GIL (เช่นใน Java, C ++ ฯลฯ ) จำเป็นต้องมีการซิงโครไนซ์อย่างชัดเจน
Alfe

1
@ user50473 หากไม่มีข้อมูลเพิ่มเติมฉันจะเข้าหางานจากด้านเธรดก่อน หนึ่งเธรดอ่านข้อมูลในตอนนี้และจากนั้นเข้าสู่โหมดสลีจนกระทั่งถึงเวลาที่ต้องทำอีกครั้ง วิธีการแก้ปัญหาข้างต้นสามารถใช้ในการทำสิ่งนี้ได้แน่นอน แต่ฉันสามารถจินตนาการเหตุผลมากมายที่จะไปในทางที่แตกต่าง ขอให้โชคดี :)
Alfe

1
สลีปสามารถแทนที่ได้ด้วยเธรดอีเวนต์รอด้วยการหมดเวลาเพื่อให้ตอบสนองได้ดียิ่งขึ้นเมื่อออกจากแอปพลิเคชัน stackoverflow.com/questions/29082268/…
themadmax

20

นี่คือการอัปเดตรหัสจาก MestreLion ที่หลีกเลี่ยงการลอยในช่วงเวลา

คลาส RepeatedTimer ที่นี่เรียกใช้ฟังก์ชันที่กำหนดทุก ๆ "ช่วงเวลา" วินาทีตามที่ร้องขอโดย OP; กำหนดการไม่ได้ขึ้นอยู่กับระยะเวลาที่ฟังก์ชันใช้ในการดำเนินการ ฉันชอบโซลูชันนี้เนื่องจากไม่มีการอ้างอิงไลบรารีภายนอก นี่เป็นเพียงไพ ธ อนแท้ๆ

import threading 
import time

class RepeatedTimer(object):
  def __init__(self, interval, function, *args, **kwargs):
    self._timer = None
    self.interval = interval
    self.function = function
    self.args = args
    self.kwargs = kwargs
    self.is_running = False
    self.next_call = time.time()
    self.start()

  def _run(self):
    self.is_running = False
    self.start()
    self.function(*self.args, **self.kwargs)

  def start(self):
    if not self.is_running:
      self.next_call += self.interval
      self._timer = threading.Timer(self.next_call - time.time(), self._run)
      self._timer.start()
      self.is_running = True

  def stop(self):
    self._timer.cancel()
    self.is_running = False

ตัวอย่างการใช้งาน (คัดลอกมาจากคำตอบของ MestreLion):

from time import sleep

def hello(name):
    print "Hello %s!" % name

print "starting..."
rt = RepeatedTimer(1, hello, "World") # it auto-starts, no need of rt.start()
try:
    sleep(5) # your long-running job goes here...
finally:
    rt.stop() # better in a try/finally block to make sure the program ends!

5

ฉันต้องเผชิญกับปัญหาที่คล้ายกันในบางเวลา อาจเป็นhttp://cronus.readthedocs.orgอาจช่วยได้?

สำหรับ v0.2 ตัวอย่างต่อไปนี้ใช้งานได้

import cronus.beat as beat

beat.set_rate(2) # 2 Hz
while beat.true():
    # do some time consuming work here
    beat.sleep() # total loop duration would be 0.5 sec

4

ข้อแตกต่างที่สำคัญระหว่างสิ่งนั้นกับ cron คือข้อยกเว้นจะทำให้ดีมอนเป็นตัวฆ่า คุณอาจต้องการห่อด้วยตัวจับข้อยกเว้นและตัวบันทึก


4

หนึ่งคำตอบที่เป็นไปได้:

import time
t=time.time()

while True:
    if time.time()-t>10:
        #run your task here
        t=time.time()

1
นี่คือการรอคอยที่ไม่ดีดังนั้นจึงไม่ดีมาก
Alfe

ทางออกที่ดีสำหรับคนที่กำลังมองหาตัวจับเวลาที่ไม่ปิดกั้น
Noel

3

ฉันลงเอยด้วยการใช้โมดูลกำหนดการ API นั้นดี

import schedule
import time

def job():
    print("I'm working...")

schedule.every(10).minutes.do(job)
schedule.every().hour.do(job)
schedule.every().day.at("10:30").do(job)
schedule.every(5).to(10).minutes.do(job)
schedule.every().monday.do(job)
schedule.every().wednesday.at("13:15").do(job)
schedule.every().minute.at(":17").do(job)

while True:
    schedule.run_pending()
    time.sleep(1)

ฉันมีปัญหาในการพยายามใช้โมดูลนี้โดยเฉพาะฉันต้องปลดบล็อกเธรดหลักฉันได้ตรวจสอบคำถามที่พบบ่อยในเว็บไซต์เอกสารกำหนดการ แต่ฉันไม่เข้าใจวิธีแก้ปัญหาที่ให้มาจริงๆ ไม่มีใครรู้ว่าฉันสามารถหาตัวอย่างการทำงานที่ไม่บล็อกเธรดหลักได้ที่ไหน
5

1

ผมใช้ Tkinter หลัง () วิธีการซึ่งไม่ได้ "ขโมยเกม" (เช่นschedโมดูลที่ถูกนำเสนอก่อนหน้านี้) คือมันจะช่วยให้สิ่งอื่น ๆ ที่จะทำงานในแบบคู่ขนาน:

import Tkinter

def do_something1():
  global n1
  n1 += 1
  if n1 == 6: # (Optional condition)
    print "* do_something1() is done *"; return
  # Do your stuff here
  # ...
  print "do_something1() "+str(n1)
  tk.after(1000, do_something1)

def do_something2(): 
  global n2
  n2 += 1
  if n2 == 6: # (Optional condition)
    print "* do_something2() is done *"; return
  # Do your stuff here
  # ...
  print "do_something2() "+str(n2)
  tk.after(500, do_something2)

tk = Tkinter.Tk(); 
n1 = 0; n2 = 0
do_something1()
do_something2()
tk.mainloop()

do_something1()และdo_something2()สามารถทำงานแบบขนานและในช่วงความเร็วใดก็ได้ ที่นี่คนที่สองจะถูกประหารชีวิตสองครั้งเร็วขึ้นนอกจากนี้ฉันยังใช้ตัวนับแบบง่าย ๆ เป็นเงื่อนไขในการยุติฟังก์ชั่นทั้งสอง คุณสามารถใช้การแข่งขันอื่นใดก็ได้ที่คุณชอบหรือไม่มีหากคุณต้องการให้ฟังก์ชันใดทำงานจนกว่าโปรแกรมจะสิ้นสุด (เช่นนาฬิกา)


ระวังข้อความของคุณ: afterไม่อนุญาตให้สิ่งต่าง ๆ ทำงานคู่ขนาน Tkinter เป็นแบบเธรดเดี่ยวและสามารถทำสิ่งหนึ่งครั้งเท่านั้น หากสิ่งที่กำหนดโดยafterกำลังทำงานอยู่จะไม่ทำงานพร้อมกับส่วนที่เหลือของรหัส หากทั้งคู่do_something1และdo_something2ถูกกำหนดให้ทำงานในเวลาเดียวกันพวกเขาจะทำงานตามลำดับไม่ใช่แบบขนาน
ไบรอัน Oakley

@Apostolos วิธีแก้ปัญหาทั้งหมดของคุณคือการใช้tkinter mainloop แทนที่จะเป็นsched mainloop ดังนั้นมันจึงทำงานในลักษณะเดียวกัน แต่อนุญาตให้อินเตอร์เฟส tkinter ตอบสนองต่อไป หากคุณไม่ได้ใช้ tkinter สำหรับสิ่งอื่น ๆ มันจะไม่เปลี่ยนแปลงอะไรเลยเกี่ยวกับโซลูชันการกำหนดเวลา คุณสามารถใช้ฟังก์ชั่นที่จัดกำหนดการไว้สองฟังก์ชันขึ้นไปที่มีช่วงเวลาต่างกันในschedโซลูชันและมันจะทำงานเหมือนกับคุณ
nosklo

ไม่มันไม่ทำงานเหมือนกัน ฉันอธิบายสิ่งนี้ หนึ่ง "ล็อค" โปรแกรม (เช่นหยุดการไหลคุณไม่สามารถทำอะไรได้เลย - ไม่ได้เริ่มงาน scecduled อื่นตามที่คุณแนะนำ) จนกว่ามันจะเสร็จสิ้นและอีกส่วนหนึ่งช่วยให้มือของคุณ / ฟรีฟรี (เช่นคุณสามารถทำได้ สิ่งอื่น ๆ หลังจากที่เริ่มต้นขึ้นคุณไม่ต้องรอจนกว่าจะเสร็จสิ้นนี่เป็นความแตกต่างที่ยิ่งใหญ่หากคุณลองวิธีที่ฉันนำเสนอคุณจะได้เห็นด้วยตัวคุณเองฉันได้ลองแล้วทำไมคุณไม่ ลองของฉันด้วยเหรอ
Apostolos

1

นี่คือเวอร์ชันที่ปรับให้เข้ากับโค้ดจาก MestreLion นอกจากฟังก์ชั่นดั้งเดิมแล้วรหัสนี้:

1) เพิ่ม first_interval ที่ใช้ในการเริ่มจับเวลาในเวลาที่กำหนด (ผู้เรียกต้องคำนวณ first_interval และส่งผ่าน)

2) แก้ปัญหาสภาพการแข่งขันในรหัสต้นฉบับ ในรหัสดั้งเดิมหากเธรดควบคุมล้มเหลวในการยกเลิกตัวจับเวลาที่ทำงานอยู่ ("หยุดตัวจับเวลาและยกเลิกการเรียกใช้การกระทำของตัวจับเวลานี่จะใช้งานได้ต่อเมื่อตัวจับเวลายังอยู่ในช่วงรอ" ยกมาจากhttps: // docs.python.org/2/library/threading.html ) ตัวจับเวลาจะทำงานไม่สิ้นสุด

class RepeatedTimer(object):
def __init__(self, first_interval, interval, func, *args, **kwargs):
    self.timer      = None
    self.first_interval = first_interval
    self.interval   = interval
    self.func   = func
    self.args       = args
    self.kwargs     = kwargs
    self.running = False
    self.is_started = False

def first_start(self):
    try:
        # no race-condition here because only control thread will call this method
        # if already started will not start again
        if not self.is_started:
            self.is_started = True
            self.timer = Timer(self.first_interval, self.run)
            self.running = True
            self.timer.start()
    except Exception as e:
        log_print(syslog.LOG_ERR, "timer first_start failed %s %s"%(e.message, traceback.format_exc()))
        raise

def run(self):
    # if not stopped start again
    if self.running:
        self.timer = Timer(self.interval, self.run)
        self.timer.start()
    self.func(*self.args, **self.kwargs)

def stop(self):
    # cancel current timer in case failed it's still OK
    # if already stopped doesn't matter to stop again
    if self.timer:
        self.timer.cancel()
    self.running = False

1

ดูเหมือนง่ายกว่าโซลูชันที่ยอมรับมาก - มีข้อบกพร่องที่ฉันไม่ได้พิจารณาหรือไม่ มาที่นี่เพื่อค้นหาพาสต้าคัดลอกที่ตายง่าย ๆ และผิดหวัง

import threading, time

def print_every_n_seconds(n=2):
    while True:
        print(time.ctime())
        time.sleep(n)

thread = threading.Thread(target=print_every_n_seconds, daemon=True)
thread.start()

ซึ่งเอาต์พุตแบบอะซิงโครนัส

#Tue Oct 16 17:29:40 2018
#Tue Oct 16 17:29:42 2018
#Tue Oct 16 17:29:44 2018

มันมีความรู้สึกในแง่ที่ว่าหากงานที่กำลังดำเนินอยู่นั้นใช้เวลาในการประเมินจำนวนช่วงเวลานั้นจะกลายเป็น 2 วินาที + เวลางานดังนั้นหากคุณต้องการกำหนดเวลาที่แน่นอนสิ่งนี้ไม่เหมาะสำหรับคุณ

หมายเหตุการdaemon=Trueตั้งค่าสถานะหมายความว่าเธรดนี้จะไม่บล็อกแอปไม่ให้ปิด ตัวอย่างเช่นมีปัญหาที่pytestจะแขวนไปเรื่อย ๆ หลังจากรันการทดสอบเพื่อรอให้ thead นี้หยุด


ไม่มันพิมพ์เฉพาะช่วงเวลาแรกและหยุดแล้ว ...
Alex Poca

คุณแน่ใจ - ฉันเพิ่งคัดลอกและวางใน terminal มันกลับมาทันที แต่งานพิมพ์ยังคงดำเนินต่อไปในพื้นหลังสำหรับฉัน
Adam Hughes

ดูเหมือนว่าฉันขาดอะไรบางอย่างที่นี่ ฉันจะคัดลอก / วางรหัสในtest.pyและทำงานกับงูหลาม test.py ด้วย Python2.7 ฉันต้องลบdaemon = Trueที่ไม่รู้จักและฉันอ่านการพิมพ์หลายครั้ง ด้วย Python3.8 มันจะหยุดทำงานหลังจากการพิมพ์ครั้งแรกและไม่มีกระบวนการใดทำงานหลังจากสิ้นสุดการทำงาน กำลังลบdaemon = จริงฉันอ่านหลายงานพิมพ์ ...
Alex Poca

อืมแปลกจัง - ฉันอยู่บนงูหลาม 3.6.10 แต่ไม่รู้ว่าทำไมมันถึงสำคัญ
Adam Hughes

อีกครั้ง: Python3.4.2 (Debian GNU / Linux 8 (เจสซี)) ต้องลบdaemon = Trueเพื่อให้สามารถพิมพ์ได้หลายครั้ง ด้วยภูต ฉันได้รับข้อผิดพลาดทางไวยากรณ์ การทดสอบก่อนหน้านี้กับ Python2.7 และ 3.8 อยู่บน Ubuntu 19.10 เป็นไปได้ไหมที่ daemon จะได้รับการปฏิบัติแตกต่างกันไปตามระบบปฏิบัติการ?
Alex Poca

0

ฉันใช้สิ่งนี้เพื่อทำให้เกิดเหตุการณ์ 60 ครั้งต่อชั่วโมงโดยที่เหตุการณ์ส่วนใหญ่เกิดขึ้นที่จำนวนวินาทีเดียวกันหลังจากทั้งนาที:

import math
import time
import random

TICK = 60 # one minute tick size
TICK_TIMING = 59 # execute on 59th second of the tick
TICK_MINIMUM = 30 # minimum catch up tick size when lagging

def set_timing():

    now = time.time()
    elapsed = now - info['begin']
    minutes = math.floor(elapsed/TICK)
    tick_elapsed = now - info['completion_time']
    if (info['tick']+1) > minutes:
        wait = max(0,(TICK_TIMING-(time.time() % TICK)))
        print ('standard wait: %.2f' % wait)
        time.sleep(wait)
    elif tick_elapsed < TICK_MINIMUM:
        wait = TICK_MINIMUM-tick_elapsed
        print ('minimum wait: %.2f' % wait)
        time.sleep(wait)
    else:
        print ('skip set_timing(); no wait')
    drift = ((time.time() - info['begin']) - info['tick']*TICK -
        TICK_TIMING + info['begin']%TICK)
    print ('drift: %.6f' % drift)

info['tick'] = 0
info['begin'] = time.time()
info['completion_time'] = info['begin'] - TICK

while 1:

    set_timing()

    print('hello world')

    #random real world event
    time.sleep(random.random()*TICK_MINIMUM)

    info['tick'] += 1
    info['completion_time'] = time.time()

ขึ้นอยู่กับเงื่อนไขจริงที่คุณอาจได้รับความยาว:

60,60,62,58,60,60,120,30,30,60,60,60,60,60...etc.

แต่เมื่อสิ้นสุด 60 นาทีคุณจะมีเห็บ 60 อัน และส่วนใหญ่จะเกิดขึ้นในช่วงเวลาที่คุณต้องการ

ในระบบของฉันฉันได้รับการดริฟท์ตามปกติที่ <1 / 20th จากวินาทีจนกระทั่งต้องมีการแก้ไขเกิดขึ้น

ข้อดีของวิธีนี้คือความละเอียดของสัญญาณนาฬิกา ซึ่งอาจทำให้เกิดปัญหาหากคุณทำสิ่งต่าง ๆ เช่นการผนวกหนึ่งรายการต่อหนึ่งขีดและคุณคาดว่าจะเพิ่ม 60 รายการต่อชั่วโมง ความล้มเหลวในการบัญชีเกี่ยวกับการดริฟท์อาจทำให้เกิดข้อบ่งชี้รองเช่นการย้ายค่าเฉลี่ยเพื่อพิจารณาข้อมูลที่ลึกเกินไปในอดีต


0

เช่นแสดงเวลาท้องถิ่นปัจจุบัน

import datetime
import glib
import logger

def get_local_time():
    current_time = datetime.datetime.now().strftime("%H:%M")
    logger.info("get_local_time(): %s",current_time)
    return str(current_time)

def display_local_time():
    logger.info("Current time is: %s", get_local_time())
    return True

# call every minute
glib.timeout_add(60*1000, display_local_time)

0
    ''' tracking number of times it prints'''
import threading

global timeInterval
count=0
def printit():
  threading.Timer(timeInterval, printit).start()
  print( "Hello, World!")
  global count
  count=count+1
  print(count)
printit

if __name__ == "__main__":
    timeInterval= int(input('Enter Time in Seconds:'))
    printit()

บนพื้นฐานของการป้อนข้อมูลของผู้ใช้มันจะทำซ้ำวิธีนั้นทุกช่วงเวลา
raviGupta

0

นี่เป็นอีกวิธีการหนึ่งโดยไม่ใช้ libaries พิเศษใด ๆ

def delay_until(condition_fn, interval_in_sec, timeout_in_sec):
    """Delay using a boolean callable function.

    `condition_fn` is invoked every `interval_in_sec` until `timeout_in_sec`.
    It can break early if condition is met.

    Args:
        condition_fn     - a callable boolean function
        interval_in_sec  - wait time between calling `condition_fn`
        timeout_in_sec   - maximum time to run

    Returns: None
    """
    start = last_call = time.time()
    while time.time() - start < timeout_in_sec:
        if (time.time() - last_call) > interval_in_sec:
            if condition_fn() is True:
                break
            last_call = time.time()
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.