65

我想在 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 没有被取消。

我怎样才能取消它?

提前致谢!

4

8 回答 8

105

您可以尝试 threading.Timer 类: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()
于 2010-02-08T16:43:17.773 回答
3

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

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


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

timer = BackgroundTimer()
timer.start()

显然,如果“做某事”需要很长时间,你需要在你的睡眠声明中适应它。但这是一个很好的近似值。

于 2010-02-08T16:44:12.110 回答
3

我四处搜索并找到了 Python电路框架,这使得等待
特定事件成为可能。

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

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

我希望你发现它和我一样有用:)
./问候

于 2013-08-05T12:18:21.923 回答
2

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

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

于 2010-02-08T16:51:45.417 回答
1

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

#!/usr/bin/env python
import time
while True:
   # Your code here
   time.sleep(60)
于 2010-02-08T16:42:52.060 回答
1

如果您想“按时”调用方法(例如每小时按时),您可以将以下思想与您选择的任何线程机制相结合:

import time

def wait(n):
    '''Wait until the next increment of n seconds'''
    x = time.time()
    time.sleep(n-(x%n))
    print(time.asctime())
于 2015-07-01T21:49:25.180 回答
1

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

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 秒执行一次。在线程的最后运行被取消。

于 2017-10-06T09:10:15.930 回答
-1

[剪断。删除了非异步版本]

要使用异步,您将使用 trio。我向所有询问异步 python 的人推荐 trio。使用特别是套接字要容易得多。使用套接字,我有一个具有 1 个读取和 1 个写入功能的托儿所,写入功能从读取功能放置的双端队列写入数据;并等待发送。下面的应用程序通过使用trio.run(function,parameters)然后打开一个托儿所来工作,在托儿所中,程序在循环中运行,await trio.sleep(60)每个循环之间都有一个循环,让应用程序的其余部分有机会运行。这将在单个进程中运行程序,但您的机器可以处理 1500 个 TCP 连接,而使用非异步方法只能处理 255 个。

我还没有掌握取消语句,但我说move_on_after(70)这意味着代码将等待 10 秒,而不是执行 60 秒的睡眠,然后再进入下一个循环。

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

于 2021-12-05T05:29:14.020 回答