Cách tốt nhất để liên tục thực hiện một chức năng mỗi x giây là gì?


281

Tôi muốn liên tục thực thi một chức năng trong Python cứ sau 60 giây (giống như một NSTimer trong Mục tiêu C). Mã này sẽ chạy như một daemon và thực sự giống như gọi tập lệnh python mỗi phút bằng cron, nhưng không yêu cầu người dùng phải thiết lập nó.

Trong câu hỏi này về một cron được triển khai trong Python , giải pháp có vẻ như chỉ cần ngủ () trong x giây. Tôi không cần chức năng nâng cao như vậy nên có lẽ một cái gì đó như thế này sẽ hoạt động

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

Có bất kỳ vấn đề có thể thấy trước với mã này?


83
Một điểm phạm vi, nhưng có thể rất quan trọng, mã trên mã của bạn không thực thi cứ sau 60 giây, nó đặt khoảng cách 60 giây giữa các lần thực thi. Nó chỉ xảy ra cứ sau 60 giây nếu mã thực thi của bạn không mất thời gian.
Simon

4
cũng time.sleep(60)có thể quay lại cả sớm hơn và muộn hơn
jfs

5
Tôi vẫn đang tự hỏi: Có bất kỳ vấn đề có thể thấy trước với mã này?
Chuối

1
"Vấn đề có thể thấy trước" là bạn không thể mong đợi 60 lần lặp mỗi giờ chỉ bằng cách sử dụng time.s ngủ (60). Vì vậy, nếu bạn đang nối thêm một mục trên mỗi lần lặp và giữ một danh sách độ dài đã đặt ... thì trung bình của danh sách đó sẽ không biểu thị một "khoảng thời gian" nhất quán; vì vậy các chức năng như "trung bình di động" có thể tham chiếu các điểm dữ liệu quá cũ, điều này sẽ làm sai lệch chỉ định của bạn.
kiện tụng

2
@Banana Có, bạn có thể mong đợi bất kỳ sự cố nào do tập lệnh của bạn không được thực thi CHÍNH XÁC cứ sau 60 giây. Ví dụ. Tôi đã bắt đầu làm một cái gì đó như thế này để phân chia các luồng video và tải lên, và cuối cùng tôi đã nhận được các chuỗi dài hơn 5-10 giây vì hàng đợi phương tiện đang đệm trong khi tôi xử lý dữ liệu trong vòng lặp. Nó phụ thuộc vào dữ liệu của bạn. Ví dụ, nếu chức năng là một loại cơ quan giám sát đơn giản cảnh báo bạn, khi đĩa của bạn đầy, bạn sẽ không gặp vấn đề gì với điều này. Nếu bạn đang kiểm tra cảnh báo nhà máy điện hạt nhân, bạn có thể kết thúc với một thành phố hoàn toàn nổ tung x
DGoiko

Câu trả lời:


227

Nếu chương trình của bạn chưa có vòng lặp sự kiện, hãy sử dụng mô-đun lịch biểu , thực hiện bộ lập lịch sự kiện cho mục đích chung.

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

Nếu bạn đã sử dụng một thư viện vòng lặp sự kiện như asyncio, trio, tkinter, PyQt5, gobject, kivy, và nhiều người khác - chỉ cần sắp xếp công việc sử dụng phương pháp hiện có thư viện vòng lặp sự kiện của bạn, để thay thế.


16
Mô-đun lịch biểu là để lập lịch các chức năng để chạy sau một thời gian, làm thế nào để bạn sử dụng nó để lặp lại một cuộc gọi hàm mỗi x giây mà không sử dụng time.s ngủ ()?
Baishampayan Ghose

2
@Baishampayan: Chỉ cần lên lịch chạy mới.
nosklo

3
Sau đó apscheduler tại packages.python.org/APScheduler cũng sẽ nhận được một đề cập đến thời điểm này.
Daniel F

6
lưu ý: phiên bản này có thể trôi. Bạn có thể sử dụng enterabs()để tránh nó. Đây là một phiên bản không trôi để so sánh .
JFS

8
@JavaSa: bởi vì "làm công cụ của bạn" không phải là tức thời và lỗi từ time.sleepcó thể tích lũy ở đây. "thực hiện mỗi X giây" và "thực hiện với độ trễ ~ X giây liên tục" không giống nhau. Xem thêm bình luận này
jfs

180

Chỉ cần khóa vòng lặp thời gian của bạn để đồng hồ hệ thống. Dễ dàng.

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

22
+1. của bạn và twistedcâu trả lời là câu trả lời duy nhất chạy một chức năng mỗi xgiây. Phần còn lại thực hiện chức năng với độ trễ xvài giây sau mỗi cuộc gọi.
jfs

13
Nếu bạn thêm một số mã vào đây mất nhiều thời gian hơn một giây ... Nó sẽ ném thời gian ra và bắt đầu tụt lại phía sau .. Câu trả lời được chấp nhận trong trường hợp này là chính xác ... Bất cứ ai cũng có thể lặp một lệnh in đơn giản và chạy nó mỗi giây không chậm trễ ...
Angry 84

5
Tôi thích from time import time, sleepvì ý nghĩa hiện sinh;)
Sẽ

14
Hoạt động tuyệt vời. Không cần phải trừ đi starttimenếu bạn bắt đầu bằng cách đồng bộ hóa nó vào một thời điểm nhất định: time.sleep(60 - time.time() % 60)đã hoạt động tốt với tôi. Tôi đã sử dụng nó time.sleep(1200 - time.time() % 1200)và nó cung cấp cho tôi nhật ký trên :00 :20 :40, chính xác như tôi muốn.
TemporalWolf

2
@AntonSchigur để tránh trôi sau nhiều lần lặp. Một lần lặp cá nhân có thể bắt đầu hơi sớm hay muộn phụ thuộc vào sleep(), timer()chính xác và phải mất bao lâu để thực hiện thân vòng lặp nhưng ở những dòng máy trung bình luôn luôn xảy ra trên ranh giới khoảng cách (ngay cả khi một số là bỏ qua): while keep_doing_it(): sleep(interval - timer() % interval). So sánh nó với while keep_doing_it(): sleep(interval)nơi lỗi có thể tích lũy sau vài lần lặp.
JFS

71

Bạn có thể muốn xem Twisted là thư viện mạng Python thực hiện Mẫu Lò phản ứng .

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

Trong khi "while True: ngủ (60)" có thể sẽ hoạt động, Twisted có thể đã triển khai nhiều tính năng mà cuối cùng bạn sẽ cần (trình bày, ghi nhật ký hoặc xử lý ngoại lệ như được chỉ ra bởi bobince) và có thể sẽ là một giải pháp mạnh mẽ hơn


Câu trả lời tuyệt vời là tốt, rất chính xác mà không trôi. Tôi tự hỏi nếu điều này làm cho CPU cũng ngủ trong khi chờ đợi để thực hiện nhiệm vụ (còn gọi là không bận rộn chờ đợi)?
Smoothware

1
trôi dạt ở cấp độ mili giây
Derek Eden

"Sự trôi dạt ở cấp độ mili giây" nghĩa là gì?
Jean-Paul Calderone

67

Nếu bạn muốn một cách không chặn để thực hiện chức năng của mình theo định kỳ, thay vì một vòng lặp vô hạn chặn, tôi sẽ sử dụng bộ đếm thời gian theo luồng. Bằng cách này, mã của bạn có thể tiếp tục chạy và thực hiện các tác vụ khác và vẫn có chức năng của bạn được gọi mỗi n giây. Tôi sử dụng kỹ thuật này rất nhiều để in thông tin tiến trình trên các tác vụ chuyên sâu về CPU / Đĩa / Mạng.

Đây là mã tôi đã đăng trong một câu hỏi tương tự, với điều khiển start () và stop ():

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

Sử dụng:

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!

Đặc trưng:

  • Chỉ thư viện chuẩn, không phụ thuộc bên ngoài
  • start()stop()an toàn để gọi nhiều lần ngay cả khi bộ hẹn giờ đã bắt đầu / dừng
  • Hàm được gọi có thể có các đối số vị trí và được đặt tên
  • Bạn có thể thay đổi intervalbất cứ lúc nào, nó sẽ có hiệu lực sau lần chạy tiếp theo. Tương tự cho args, kwargsvà thậm chí function!

Giải pháp này dường như trôi theo thời gian; Tôi cần một phiên bản nhằm mục đích gọi hàm mỗi n giây mà không bị trôi. Tôi sẽ đăng một bản cập nhật trong một câu hỏi riêng biệt.
kỷ nguyên

Trong def _run(self)tôi đang cố quấn đầu xung quanh tại sao bạn gọi self.start()trước self.function(). Bạn có thể xây dựng? Tôi nghĩ rằng bằng cách gọi start()đầu tiên self.is_runningsẽ luôn luôn Falsenhư vậy sau đó chúng tôi sẽ luôn luôn tạo ra một chủ đề mới.
Episcopo phong phú

1
Tôi nghĩ rằng tôi đã đến đáy của nó. Giải pháp của @ MestreLion chạy một hàm mỗi xgiây (tức là t = 0, t = 1x, t = 2x, t = 3x, ...) trong đó tại mã mẫu áp phích ban đầu chạy một hàm với khoảng x giây ở giữa. Ngoài ra, giải pháp này tôi tin là có lỗi nếu intervalngắn hơn thời gian functionthực hiện. Trong trường hợp đó, self._timersẽ được ghi đè trong starthàm.
Episcopo giàu

Có, @RichieEpiscopo, cuộc gọi đến .function()sau .start()là để chạy chức năng tại t = 0. Và tôi không nghĩ nó sẽ là một vấn đề nếu functionmất nhiều thời gian hơn interval, nhưng vâng, có thể có một số điều kiện đua trên mã.
MestreLion

Đây là cách duy nhất không chặn tôi có thể nhận được. Cảm ơn.
dấu gạch chéo ngược

35

Cách dễ dàng hơn tôi tin là:

import time

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

while True:
    executeSomething()

Bằng cách này, mã của bạn được thực thi, sau đó nó đợi 60 giây rồi nó thực thi lại, chờ, thực thi, v.v ... Không cần phức tạp hóa mọi thứ: D


Từ khóa True phải là chữ hoa
Sean Cain

38
Trên thực tế đây không phải là câu trả lời: thời gian ngủ () chỉ có thể được sử dụng để chờ X giây sau mỗi lần thực hiện. Ví dụ: nếu chức năng của bạn mất 0,5 giây để thực thi và bạn sử dụng time.s ngủ (1), điều đó có nghĩa là chức năng của bạn thực thi cứ sau 1,5 giây chứ không phải 1. Bạn nên sử dụng các mô-đun và / hoặc luồng khác để đảm bảo có gì đó hoạt động trong thời gian Y trong mỗi X giây.
kommradHomer

1
@kommradHomer: Câu trả lời của Dave Rove chứng minh rằng bạn có thể sử dụng time.sleep()chạy thứ gì đó cứ sau X giây
jfs

2
Theo tôi, mã nên gọi time.sleep()theo while Truevòng lặp như:def executeSomething(): print('10 sec left') ; while True: executeSomething(); time.sleep(10)
Leonard 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)

Nếu bạn muốn làm điều này mà không chặn mã còn lại của mình, bạn có thể sử dụng mã này để cho phép nó chạy trong luồng của chính nó:

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

Giải pháp này kết hợp một số tính năng hiếm khi được tìm thấy kết hợp trong các giải pháp khác:

  • Xử lý ngoại lệ: Càng xa càng tốt ở cấp độ này, các ngoại lệ được xử lý đúng cách, tức là được ghi nhật ký cho mục đích gỡ lỗi mà không hủy bỏ chương trình của chúng tôi.
  • Không có chuỗi: Việc triển khai giống như chuỗi chung (để lên lịch cho sự kiện tiếp theo) mà bạn tìm thấy trong nhiều câu trả lời là dễ vỡ ở khía cạnh nếu có bất cứ điều gì sai trong cơ chế lập lịch ( threading.Timerhoặc bất cứ điều gì), điều này sẽ chấm dứt chuỗi. Không có vụ xử tử nào nữa sẽ xảy ra sau đó, ngay cả khi lý do của vấn đề đã được khắc phục. Một vòng lặp đơn giản và chờ đợi với một đơn giản sleep()là mạnh mẽ hơn nhiều so với.
  • Không trôi: Giải pháp của tôi theo dõi chính xác thời gian cần chạy. Không có trôi tùy thuộc vào thời gian thực hiện (như trong nhiều giải pháp khác).
  • Bỏ qua: Giải pháp của tôi sẽ bỏ qua các tác vụ nếu một lần thực hiện mất quá nhiều thời gian (ví dụ: thực hiện X cứ sau 5 giây, nhưng X mất 6 giây). Đây là hành vi cron tiêu chuẩn (và vì một lý do tốt). Nhiều giải pháp khác sau đó chỉ cần thực hiện nhiệm vụ nhiều lần liên tiếp mà không có bất kỳ sự chậm trễ nào. Đối với hầu hết các trường hợp (ví dụ: nhiệm vụ dọn dẹp), điều này không được mong muốn. Nếu nó mong muốn, chỉ cần sử dụng next_time += delaythay thế.

2
Câu trả lời tốt nhất cho việc không trôi.
Sebastian Stark

1
@PirateApp Tôi sẽ làm điều này trong một chủ đề khác. Bạn có thể làm điều đó trong cùng một chủ đề nhưng sau đó bạn kết thúc việc lập trình hệ thống lập lịch trình của riêng bạn, điều này quá phức tạp cho một nhận xét.
Alfe

1
Trong Python, nhờ GIL, truy cập các biến trong hai luồng là hoàn toàn an toàn. Và chỉ đọc trong hai luồng không bao giờ là một vấn đề (cũng không phải trong các môi trường luồng khác). Chỉ viết từ hai luồng khác nhau trong một hệ thống không có GIL (ví dụ: trong Java, C ++, v.v.) cần một số đồng bộ hóa rõ ràng.
Alfe

1
@ user50473 Nếu không có thêm thông tin nào, trước tiên tôi sẽ tiếp cận tác vụ từ phía luồng. Một luồng đọc dữ liệu bây giờ và sau đó ngủ cho đến khi nó lại làm việc đó. Các giải pháp trên có thể được sử dụng để làm điều này tất nhiên. Nhưng tôi có thể tưởng tượng ra một loạt lý do để đi một con đường khác.
Chúc

1
Giấc ngủ có thể được thay thế bằng cách xâu chuỗi. Hãy chờ đợi với thời gian chờ để phản ứng nhanh hơn khi thoát ứng dụng. stackoverflow.com/questions/29082268/
hy

20

Đây là bản cập nhật mã từ MestreLion để tránh trôi dạt theo thời gian.

Lớp RepeatTimer ở ​​đây gọi hàm đã cho mỗi "giây" theo yêu cầu của OP; lịch trình không phụ thuộc vào thời gian thực hiện chức năng. Tôi thích giải pháp này vì nó không có phụ thuộc thư viện bên ngoài; Đây chỉ là con trăn thuần túy.

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

Sử dụng mẫu (được sao chép từ câu trả lời của 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

Tôi đã phải đối mặt với một vấn đề tương tự một thời gian trở lại. Có thể http://cronus.readthedocs.org có thể giúp đỡ?

Đối với v0.2, đoạn mã sau hoạt động

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

Sự khác biệt chính giữa điều đó và cron là một ngoại lệ sẽ giết daemon mãi mãi. Bạn có thể muốn bọc với một công cụ bắt và ghi nhật ký ngoại lệ.


4

Một câu trả lời có thể có:

import time
t=time.time()

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

1
Đây là bận rộn chờ đợi vì vậy rất xấu.
Alfe

Giải pháp tốt cho ai đó đang tìm kiếm một bộ đếm thời gian không chặn.
Noel

3

Tôi đã kết thúc bằng cách sử dụng các mô-đun lịch trình . API rất hay.

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)

Tôi đang gặp khó khăn khi cố gắng sử dụng mô-đun này, đặc biệt, tôi cần bỏ chặn luồng chính, tôi đã kiểm tra Câu hỏi thường gặp trong trang web tài liệu của lịch biểu, nhưng tôi không thực sự hiểu cách giải quyết được cung cấp. Có ai biết nơi tôi có thể tìm thấy một ví dụ hoạt động không chặn luồng chính không?
5Daydreams

1

Tôi sử dụng phương thức Tkinter after (), không "đánh cắp trò chơi" (như mô-đun lịch trình đã được trình bày trước đó), tức là nó cho phép các thứ khác chạy song song:

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()có thể chạy song song và ở bất kỳ tốc độ nào. Ở đây, cái thứ 2 sẽ được thực thi nhanh gấp đôi. Cũng xin lưu ý rằng tôi đã sử dụng một bộ đếm đơn giản như một điều kiện để chấm dứt một trong hai chức năng. Bạn có thể sử dụng bất kỳ đối số nào khác mà bạn thích hoặc không có gì nếu bạn chạy chức năng nào cho đến khi chương trình kết thúc (ví dụ: đồng hồ).


Hãy cẩn thận với từ ngữ của bạn: afterkhông cho phép mọi thứ chạy song song. Tkinter là một luồng đơn và chỉ có thể làm một việc tại một thời điểm. Nếu một cái gì đó được lên lịch bởi afterđang chạy, thì nó không chạy song song với phần còn lại của mã. Nếu cả hai do_something1do_something2được lên lịch để chạy cùng một lúc, chúng sẽ chạy tuần tự, không song song.
Bryan Oakley

@Apostolos tất cả giải pháp của bạn là sử dụng mainloop tkinter thay vì lên lịch mainloop, vì vậy nó hoạt động chính xác theo cùng một cách nhưng cho phép các giao diện tkinter tiếp tục phản hồi. Nếu bạn không sử dụng tkinter cho những thứ khác thì nó sẽ không thay đổi bất cứ điều gì liên quan đến giải pháp lịch trình. Bạn có thể sử dụng hai hoặc nhiều hàm được lên lịch với các khoảng khác nhau trong schedgiải pháp và nó sẽ hoạt động giống hệt như của bạn.
nosklo

Không, nó không hoạt động theo cùng một cách. Tôi đã giải thích điều này. Một "khóa" chương trình (tức là dừng dòng chảy, bạn không thể làm gì khác - thậm chí không bắt đầu một công việc được quy định khác như bạn đề xuất) cho đến khi chương trình kết thúc và chương trình còn lại cho phép bạn rảnh tay (tức là bạn có thể làm Những thứ khác sau khi nó bắt đầu. Bạn không cần phải đợi cho đến khi nó kết thúc. Đây là một sự khác biệt lớn. Nếu bạn đã thử phương pháp tôi đã trình bày, bạn sẽ tự mình nhìn thấy. Tôi đã thử của bạn. Tại sao bạn không thử của tôi quá?
Apostolos

1

Đây là phiên bản phù hợp với mã từ MestreLion. Ngoài chức năng ban đầu, mã này:

1) thêm first_interval được sử dụng để kích hoạt bộ đếm thời gian tại một thời điểm cụ thể (người gọi cần tính toán First_interval và truyền vào)

2) giải quyết một điều kiện cuộc đua trong mã gốc. Trong mã gốc, nếu luồng điều khiển không thể hủy bộ hẹn giờ đang chạy ("Dừng bộ hẹn giờ và hủy thực thi hành động của bộ hẹn giờ. Điều này sẽ chỉ hoạt động nếu bộ hẹn giờ vẫn ở giai đoạn chờ." Trích dẫn từ https: // docs.python.org/2/l Library / threading.html ), bộ đếm thời gian sẽ chạy vô tận.

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

Điều này có vẻ đơn giản hơn nhiều so với giải pháp được chấp nhận - nó có thiếu sót gì không? Đến đây để tìm kiếm một số mì ống sao chép đơn giản và đã thất vọng.

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

Mà đầu ra không đồng bộ.

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

Điều này có nghĩa là nếu tác vụ đang chạy chiếm một lượng thời gian đáng kể, thì khoảng thời gian đó sẽ là 2 giây + thời gian tác vụ, vì vậy nếu bạn cần lập lịch chính xác thì đây không phải là dành cho bạn.

Lưu ý daemon=Truecờ có nghĩa là chủ đề này sẽ không chặn ứng dụng tắt. Ví dụ, có vấn đề pytestsẽ treo vô thời hạn sau khi chạy các bài kiểm tra chờ cho đến khi kết thúc này.


Không, nó chỉ in datetime đầu tiên và sau đó dừng lại ...
Alex Poca

Bạn có chắc chắn - Tôi chỉ sao chép và dán trong thiết bị đầu cuối. Nó trả về ngay lập tức nhưng bản in vẫn tiếp tục trong nền cho tôi.
Adam Hughes

Có vẻ như tôi đang thiếu một cái gì đó ở đây. Tôi copy / dán mã trong test.py , và chạy với python test.py . Với Python2.7 tôi cần xóa daemon = True không được nhận dạng và tôi đọc nhiều bản in. Với Python3.8, nó dừng sau lần in đầu tiên và không có quá trình nào được kích hoạt sau khi kết thúc. Xóa daemon = Đúng Tôi đọc nhiều bản in ...
Alex Poca

hmm lạ - tôi đang dùng python 3.6.10 nhưng không biết tại sao điều đó lại quan trọng
Adam Hughes

Một lần nữa: Python3.4.2 (Debian GNU / Linux 8 (jessie)), đã phải xóa daemon = True để nó có thể in nhiều lần. Với daemon tôi nhận được một lỗi cú pháp. Các thử nghiệm trước đây với Python2.7 và 3.8 là trên Ubuntu 19.10. Có thể là daemon được xử lý khác nhau theo HĐH?
Alex Poca

0

Tôi sử dụng điều này để gây ra 60 sự kiện mỗi giờ với hầu hết các sự kiện xảy ra ở cùng một số giây sau toàn bộ phút:

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

Tùy thuộc vào điều kiện thực tế, bạn có thể nhận được dấu vết của chiều dài:

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

nhưng cuối 60 phút bạn sẽ có 60 tích tắc; và hầu hết trong số chúng sẽ xảy ra ở độ lệch chính xác đến phút bạn thích.

Trên hệ thống của tôi, tôi nhận được độ trôi điển hình <1/20 của giây cho đến khi cần điều chỉnh.

Ưu điểm của phương pháp này là độ phân giải của đồng hồ trôi; điều này có thể gây ra vấn đề nếu bạn đang làm những việc như nối thêm một mục trên mỗi đánh dấu và bạn mong đợi 60 mục được thêm vào mỗi giờ. Việc không tính đến sự trôi dạt có thể khiến các chỉ dẫn thứ cấp như di chuyển trung bình để xem xét dữ liệu quá sâu vào quá khứ dẫn đến đầu ra bị lỗi.


0

ví dụ: Hiển thị giờ địa phương hiện tại

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

Trên cơ sở đầu vào của người dùng, nó sẽ lặp lại phương thức đó ở mọi khoảng thời gian.
raviGupta

0

Đây là một giải pháp khác mà không cần sử dụng bất kỳ libaries thêm.

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()
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.