簡體   English   中英

使用 asyncio 在每分鍾的開始(00 秒)運行一個函數

[英]Using asyncio to run a function at the start (00 seconds) of every minute

我正在嘗試使用不同的參數同時(大約或當然)運行多個函數,並在每分鍾開始時重復。

我設法得到了一個asyncio示例來運行,在那里我得到一個函數callback ,以使用不同的參數在特定時間運行,但我無法弄清楚如何在非常特定的時間運行它(並永遠運行它)(即我想在每分鍾開始時運行它,所以在 19:00:00、19:01:00 等等)。

Asyncio call_at應該能夠做到這一點,但它使用的時間格式不是標准的 python 時間格式,我無法弄清楚將該時間格式指定為下一分鍾的 00 秒。

import asyncio
import time


def callback(n, loop, msg):
    print(msg)
    print('callback {} invoked at {}'.format(n, loop.time()))


async def main(loop):
    now = loop.time()
    print('clock time: {}'.format(time.time()))
    print('loop  time: {}'.format(now))

    print('registering callbacks')
    loop.call_at(now + 0.2, callback, 1, loop, 'a')
    loop.call_at(now + 0.1, callback, 2, loop, 'b')
    loop.call_soon(callback, 3, loop, 'c')

    await asyncio.sleep(1)


event_loop = asyncio.get_event_loop()
try:
    print('entering event loop')
    event_loop.run_until_complete(main(event_loop))
finally:
    print('closing event loop')
    event_loop.close()

正如其他人所指出的,這種事情沒有內置功能,您需要自己編寫。 不過,實現起來很簡單 - 一個簡單的版本可能如下所示:

import asyncio, datetime

async def at_minute_start(cb):
    while True:
        now = datetime.datetime.now()
        after_minute = now.second + now.microsecond / 1_000_000
        if after_minute:
            await asyncio.sleep(60 - after_minute)
        cb()

這不使用call_later ,它是一個可以在不再需要時取消的協程。 它只是獲取當前掛鍾時間x的秒值,然后休眠(60 - x)以到達下一分鍾。 這是一個測試:

import time
loop = asyncio.get_event_loop()
loop.create_task(at_minute_start(lambda: print(time.asctime())))
loop.run_forever()

# output:
Wed Feb 28 21:36:00 2018
Wed Feb 28 21:37:00 2018
Wed Feb 28 21:38:00 2018
...

不幸的是,如果asyncio.sleep碰巧睡眠時間比請求的周期一點,例如由於時鍾偏差,那么簡單的實現可能會出錯。 在這種情況下,后續的asyncio.sleep將嘗試再次到達同一分鍾的開始,並且只休眠幾分之一秒,從而導致回調有效地連續快速觸發兩次。 為了防止這種情況,需要額外的代碼來補償短暫的睡眠:

async def at_minute_start(cb):
    now = datetime.datetime.now()
    wait_for_next_minute = False
    while True:
        after_minute = now.second + now.microsecond / 1_000_000
        if after_minute != 0:
            to_next_minute = 60 - after_minute
        else:
            to_next_minute = 0  # already at the minute start
        if wait_for_next_minute:
            to_next_minute += 60
        await asyncio.sleep(to_next_minute)
        cb()
        prev = now
        now = datetime.datetime.now()
        # if we're still at the same minute, our sleep was slightly
        # too short, so we'll need to wait an additional minute
        wait_for_next_minute = now.minute == prev.minute

就像一些評論員所說的那樣,在純 Python 中僅使用 asyncIO 沒有簡單的彈性方法來做到這一點,但是使用apscheduler庫實際上變得非常容易。

import asyncio
import datetime
import os

from apscheduler.schedulers.asyncio import AsyncIOScheduler


def tick():
    print("Tick! The time is: %s" % datetime.datetime.now())


if __name__ == "__main__":
    scheduler = AsyncIOScheduler()
    scheduler.add_job(tick, "cron", minute="*")
    scheduler.start()
    print("Press Ctrl+{0} to exit".format("Break" if os.name == "nt" else "C"))

    # Execution will block here until Ctrl+C (Ctrl+Break on Windows) is pressed.
    try:
        asyncio.get_event_loop().run_forever()
    except (KeyboardInterrupt, SystemExit):
        pass

沒有簡單的方法可以做到這一點。 您不能依賴loop.time()作為“真實世界”時鍾。

唯一的解決方法是使用datetime / time模塊計算loop.call_later並使用計算的延遲調用loop.call_later 是的,它超級麻煩。

看看這個問題的例子: 如何使用 asyncio 定期執行函數?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM