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

ocebsuys  于 2022-12-28  发布在  Python
关注(0)|答案(8)|浏览(370)

我想在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没有被取消。
我怎样才能取消呢?

e3bfsja2

e3bfsja21#

我在谷歌上搜索了一下,找到了Python circuits框架,它可以让我们等待
为了一个特殊的事件。
电路的.callEvent(self, event, *channels)方法包含激发和挂起直到响应功能,文档说明:
将给定事件触发到指定通道并挂起执行,直到事件被调度为止。此方法只能作为处理程序顶级执行级别上yield的参数调用(例如“yield self.callEvent(event)“)。它有效地创建并返回一个生成器,该生成器将由主循环调用,直到事件被调度为止(请参见:func:circuits.core.handlers.handler)。
希望你和我一样觉得它有用:)
./问候

0s7z1bwu

0s7z1bwu2#

这取决于你实际上想在同一时间做什么,线程是最通用和最不受欢迎的方式;在使用线程时,您应该注意线程的问题:不是所有的(非Python)代码都允许同时从多个线程访问,线程之间的通信应该使用线程安全的数据结构(如Queue.Queue)来完成,您不能从线程外部中断线程,并且在线程仍在运行时终止程序可能会导致解释器挂起或虚假的回溯。
通常有一个更简单的方法。如果你在GUI程序中这样做,使用GUI库的计时器或事件功能。所有的GUI都有这个功能。同样,如果你使用另一个事件系统,比如Twisted或另一个服务器进程模型,你应该能够挂钩到主事件循环,使它定期调用你的函数。线程方法确实会在函数挂起时导致程序被阻塞,但在函数调用之间不会。

3hvapo4f

3hvapo4f3#

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

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

eulz3vhy4#

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

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秒时间内每3秒执行一次。sleep(10)。最后取消线程的运行。

busg9geu

busg9geu5#

如果您希望“在时钟上”调用方法(例如,每小时 * 在整点 *),则可以将以下思想与您选择的任何线程机制集成:

import time

def wait(n):
    '''Wait until the next increment of n seconds'''
    x = time.time()
    time.sleep(n-(x%n))
    print(time.asctime())
sg24os4d

sg24os4d6#

[删除非异步版本]
要使用异步,你可以使用trio。我推荐trio给每一个询问async python的人。它更容易工作,尤其是套接字。对于套接字,我有一个托儿所,有一个读和一个写函数,写函数从一个双端队列写入数据,它是由读函数放置的;并等待发送。下面的应用程序的工作原理是使用trio.run(function,parameters),然后打开一个托儿所,在那里程序在循环中运行,每个循环之间有一个await trio.sleep(60),以便给应用程序的其余部分一个运行的机会。这将在单个进程中运行程序,但您的机器可以处理1500个TCP连接,而非使用非异步方法时只有255个。
我还没有掌握cancellation语句,但是我输入了move_on_after(70),这意味着代码在进入下一个循环之前将比执行60秒休眠多等待10秒。

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是易于使用功能的拥护者。它基于使用函数而不是对象,但如果您愿意,也可以使用对象。阅读更多信息,请访问:[1]:https://trio.readthedocs.io/en/stable/reference-core.html

zpjtge22

zpjtge227#

You could try the threading.Timer class: 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()
slmsl1lt

slmsl1lt8#

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

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

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

timer = BackgroundTimer()
timer.start()

显然,如果“做某件事”需要很长时间,你就需要在睡眠陈述中对此进行调整,但这是一个很好的近似值。

相关问题