如何在Python中每60秒异步执行一个函数?

How to execute a function asynchronously every 60 seconds in Python?

提问人:aF. 提问时间:2/9/2010 最后编辑:starballaF. 更新时间:2/14/2023 访问量:89610

问:

我想在 Python 上每 60 秒执行一次函数,但同时我不想被阻止。

如何异步执行此操作?

import threading
import time

def f():
    print("hello world")
    threading.Timer(3, f).start()

if __name__ == '__main__':
    f()    
    time.sleep(20)

使用此代码,函数 f 在 20 秒的 time.time 内每 3 秒执行一次。 最后它给出了一个错误,我认为这是因为threading.timer尚未被取消。

我怎样才能取消它?

Python 函数 异步 调用

评论

0赞 Thomas Wouters 2/9/2010
程序在生成第一个计时器后终止。
0赞 Thomas Wouters 2/9/2010
为了取消 Timer,您需要在某处保留对它的引用,并调用它的方法。cancel
0赞 Peter Hansen 2/9/2010
请注意,到目前为止发布的所有解决方案都存在(小的)累积错误。也就是说,他们不是根据固定的开始时间计算下一个睡眠持续时间,而是简单地睡“另一个 60 秒”。这永远不会睡在60秒以下,通常会睡得略高于60秒。这并不总是一个问题,但如果你想在一天内睡 1440 次,请确保你补偿了这个错误。
0赞 aF. 2/9/2010
这在我的解决方案中不是问题。感谢您指出这一点!
0赞 jfs 7/26/2013
相关:Python 中的 setTimeout()setInterval()。tkinter、twisted、gtk 的示例

答:

1赞 Aif 2/9/2010 #1

为什么不创建一个专用线程,在其中放置一个简单的睡眠循环:

#!/usr/bin/env python
import time
while True:
   # Your code here
   time.sleep(60)

评论

2赞 Aif 2/9/2010
不,但就是这个想法。当我回答时,没有代码,所以这只是一个想法。
0赞 Dunatotatos 4/4/2017
@MikeGraham 你能解释一下为什么它不是 Python 吗?
0赞 Mike Graham 4/6/2017
@Dunatotatos 当时的修订 stackoverflow.com/revisions/2223178/2 语法不正确。
108赞 David Underhill 2/9/2010 #2

您可以尝试线程。计时器类:http://docs.python.org/library/threading.html#timer-objects

import threading

def f(f_stop):
    # do something here ...
    if not f_stop.is_set():
        # call f() again in 60 seconds
        threading.Timer(60, f, [f_stop]).start()

f_stop = threading.Event()
# start calling f now and every 60 sec thereafter
f(f_stop)

# stop the thread when needed
#f_stop.set()

评论

0赞 aF. 2/9/2010
对于该代码,它只执行一次,是否缺少执行更多次的东西?
0赞 David Underhill 2/9/2010
哎呀,感谢您添加 .start() ...我应该从我的口译员:)复制/粘贴。现在,它应该每 60 秒调用一次,而不仅仅是一次。
0赞 aF. 2/9/2010
以及如何取消线程。程序执行结束后的计时器?
0赞 David Underhill 2/9/2010
如果程序正在结束,那么调用 sys.exit(0) 也会导致线程终止。或者,您可以使用 f() 读取的外部布尔变量。然后 f() 可以根据其值决定是否创建另一个计时器。不过,sys.exit 会更直接(因为它会立即停止另一个线程,而不是在 f() 再次运行之前等待另一个间隔)。
2赞 zch 11/3/2012
@DavidUnderhill @aF。谢谢你的帮助。我遇到了同样的问题,尽管我结束了程序,但功能仍在继续运行。在我的代码中应该去哪里?在函数体中,还是在调用函数之后 - 在这种情况下是行?sys.exit(0)f()
6赞 0xfe 2/9/2010 #3

最简单的方法是创建一个每 60 秒运行一次的后台线程。一个简单的实现是:

import time
from threading import Thread

class BackgroundTimer(Thread):   
   def run(self):
      while 1:
        time.sleep(60)
        # do something


# ... SNIP ...
# Inside your main thread
# ... SNIP ...

timer = BackgroundTimer()
timer.start()

显然,如果“做某事”需要很长时间,那么你需要在睡眠陈述中适应它。但是,60 秒是一个很好的近似值。

评论

0赞 Thomas Wouters 2/9/2010
我希望你意识到空方法是多余的:)__init__
0赞 Mike DeSimone 2/9/2010
独立于执行时间的方法是使用实时时钟(例如,或),检查任务执行开始时的时间,添加 60 秒以获得下一次触发的时间,然后在结束时再次检查并从下一次触发时间中减去它以获得您的睡眠时间。time.time()datetime.now()
2赞 Thomas Wouters 2/9/2010 #4

这取决于你在此期间实际想做什么。线程是最通用和最不喜欢的方法;使用线程时,您应该注意线程的问题:并非所有(非 Python)代码都允许同时从多个线程访问,线程之间的通信应使用线程安全的数据结构完成,例如 ,您将无法从线程外部中断线程,并且在线程仍在运行时终止程序可能会导致解释器挂起或虚假回溯。Queue.Queue

通常有一种更简单的方法。如果您在 GUI 程序中执行此操作,请使用 GUI 库的计时器或事件功能。所有 GUI 都有这个。同样,如果你使用的是另一个事件系统,比如 Twisted 或其他服务器进程模型,你应该能够挂接到主事件循环中,让它定期调用你的函数。非线程方法确实会导致程序在函数挂起时被阻塞,但在函数调用之间不会被阻塞。

评论

0赞 Mike DeSimone 2/9/2010
在 GUI 计时器上执行此操作的陷阱是,在处理超时时,GUI 被冻结。如果任务很短,没问题。如果没有,则您的程序似乎每分钟挂起一次。
3赞 synner 8/5/2013 #5

我用谷歌搜索了一下,找到了 Python circuits Framework,它可以等待
特定事件。

电路方法包含触发和暂停直到响应功能,文档说:.callEvent(self, event, *channels)

将给定事件触发到指定通道并暂停执行 直到它被发送。此方法只能作为 参数添加到处理程序的顶层执行级别(例如 "").它有效地创建和返回 一个生成器,将由主循环调用,直到事件 已调度(参见 :func:)。yieldyield self.callEvent(event)circuits.core.handlers.handler

我希望你和我一样:)发现它有用

1赞 user1054050 7/2/2015 #6

如果你想调用“on the clock”方法(例如,每小时一小时),你可以将以下想法与你选择的任何线程机制集成:

import time

def wait(n):
    '''Wait until the next increment of n seconds'''
    x = time.time()
    time.sleep(n-(x%n))
    print(time.asctime())
2赞 syviad 10/6/2017 #7

我认为重复运行线程的正确方法是下一个:

import threading
import time

def f():
    print("hello world")  # your code here
    myThread.run()

if __name__ == '__main__':
    myThread = threading.Timer(3, f)  # timer is set to 3 seconds
    myThread.start()
    time.sleep(10)  # it can be loop or other time consuming code here
    if myThread.is_alive():
        myThread.cancel()

使用此代码,函数 f 在 10 秒的 time.sleep(10) 内每 3 秒执行一次。最后,线程的运行被取消。

0赞 bauderr 12/5/2021 #8

[删除了非异步版本]

要使用异步,您将使用 trio。我向所有询问异步 python 的人推荐 trio。特别是使用插座要容易得多。使用套接字,我有一个具有 1 个读取和 1 个写入函数的托儿所,写入函数从读取函数放置数据的位置写入数据;并等待发送。以下应用的工作原理是使用并打开一个托儿所,其中程序在循环中运行,每个循环之间都有一个循环,以便为应用的其余部分提供运行机会。这将在单个进程中运行程序,但您的计算机可以使用非异步方法处理 1500 个 TCP 连接,其中只有 255 个。trio.run(function,parameters)await trio.sleep(60)

我还没有掌握取消语句,但我把它放在这意味着代码将等待 10 秒的时间比执行 60 秒的睡眠时间长,然后再进入下一个循环。move_on_after(70)

import trio
async def execTimer():
    '''This function gets executed in a nursery simultaneously with the rest of the program'''
        while True:
            trio.move_on_after(70):
                await trio.sleep(60)
                print('60 Second Loop')

async def OneTime_OneMinute():
    '''This functions gets run by trio.run to start the entire program'''
    with trio.open_nursery() as nursery:
        nursery.start_soon(execTimer)
        nursery.start_soon(print,'do the rest of the program simultaneously')


def start():
    '''You many have only one trio.run in the entire application'''
    trio.run(OneTime_OneMinute)

if __name__ == '__main__':
    start()

这将在托儿所中同时运行任意数量的功能。您可以将任何可取消语句用于程序其余部分继续运行的检查点。所有 trio 语句都是检查点,因此请经常使用它们。我没有测试这个应用程序;因此,如果有任何问题,请提出。 如您所见,trio是易于使用的功能的拥护者。它基于使用函数而不是对象,但如果您愿意,可以使用对象。 更多信息,请访问: [1]:https://trio.readthedocs.io/en/stable/reference-core.html

评论

0赞 bauderr 7/25/2023
当它使用 keywors async 和 await 时,它怎么不是异步的?