每隔n秒执行某项任务,但不使用while循环怎么做?

4

嗯,我正在尝试为Blender编写一个插件,我需要每隔n秒执行一些操作,但是我不能使用while循环,因为它会冻结Blender!我该怎么办?


我不太懂Python,但你不能用定时器和处理程序来实现吗! - mu_sa
为什么这个问题被关闭了?如果您正在使用Blender并且直接涉及编程语言,它是完全有效的。 - AMACB
这就是 Stack Overflow 的工作方式... :P - Antoni4040
4个回答

2
from threading import Timer

def doSomeThings():
    print "Things are being done"

t = Timer(5.0, doSomeThings)  # every 5 seconds
t.start()

是的,但我希望它每n秒钟持续执行... - Antoni4040
如何停止这个? - Andy Hayden
@Antoni4040 将 5.0 替换为 n - Andy Hayden
1
@hayden - 你可以调用计时器的 cancel 方法 - https://dev59.com/p2kw5IYBdhLWcg3wkLMX - Buggabill
1
好的,这只是在5秒后打印它,但不是连续的... - Antoni4040

1

来自Blender API文档中“threading”模块使用时出现的奇怪错误:

在Blender中使用Python中的线程,只有在线程在脚本运行之前完成时才能正常工作。例如,可以使用“threading.join()”。

注意: Python的线程仅允许并发,并不会加速多处理器系统上的脚本运行。subprocess和multiprocess模块可以与Blender一起使用,并可利用多个CPU。

from threading import Thread, Event

class Repeat(Thread):
    def __init__(self,delay,function,*args,**kwargs):
        Thread.__init__(self)
        self.abort = Event()
        self.delay = delay
        self.args = args
        self.kwargs = kwargs
        self.function = function
    def stop(self):
        self.abort.set()
    def run(self):
        while not self.abort.isSet():
            self.function(*self.args,**self.kwargs)
            self.abort.wait(self.delay)

例子:

from time import sleep
def do_work(foo):
    print "busy", foo
r = Repeat(1,do_work,3.14) # execute do_work(3.14) every second
r.start() # start the thread
sleep(5)  # let this demo run for 5s
r.stop()  # tell the thread to wake up and stop
r.join()  # don't forget to .join() before your script ends

1
根据您的需求,time.sleepthreading.Timer可能可以胜任。
如果您需要更全面的调度程序,我最喜欢的版本是在http://code.activestate.com/recipes/496800-event-scheduling-threadingtimer/中找到的配方。
import thread
import threading

class Operation(threading._Timer):
    def __init__(self, *args, **kwargs):
        threading._Timer.__init__(self, *args, **kwargs)
        self.setDaemon(True)

    def run(self):
        while True:
            self.finished.clear()
            self.finished.wait(self.interval)
            if not self.finished.isSet():
                self.function(*self.args, **self.kwargs)
            else:
                return
            self.finished.set()

class Manager(object):

    ops = []

    def add_operation(self, operation, interval, args=[], kwargs={}):
        op = Operation(interval, operation, args, kwargs)
        self.ops.append(op)
        thread.start_new_thread(op.run, ())

    def stop(self):
        for op in self.ops:
            op.cancel()

class LockPrint(object):
    def __init__(self):
        self.lock = threading.Lock()
    def lprint(self, value):
        with self.lock:
            print value

if __name__ == '__main__':
    import time
    import datetime

    lp = LockPrint()

    def hello1():
        lp.lprint('{}\thello1!'.format(datetime.datetime.now()))
    def hello2():
        lp.lprint('{}\thello2!'.format(datetime.datetime.now()))
    def hello3_blocking(): # this is bad, so don't do it in real code ;)
        lp.lprint('{}\thello3_blocking starting... '.format(
            datetime.datetime.now()
        )),
        t = time.time() # get a timestamp
        x = 0
        while time.time() - t < 3: # iterate in a blocking loop for 3 secs
            x += 1
        lp.lprint('{}\thello3_blocking complete! ({} iterations)'.format(
            datetime.datetime.now(), x
        ))



    timer = Manager()
    timer.add_operation(hello1, 1)
    timer.add_operation(hello2, 2)
    timer.add_operation(hello3_blocking, 2)

    t0 = time.time()
    while time.time() - t0 < 10:
        time.sleep(0.1)
    # turn off everything and exit...
    timer.stop()

一般来说,这是时间安全的,因为每个操作都在一个线程下执行,主线程仍然可以在单个操作线程中切换出阻塞部分,并维护其他操作的计划(假设您的函数不会引发任何异常直到解释器,打破主调度程序线程...)

我不确定这在blender中的行为如何,但在我使用过的其他环境中(特别是基于tornado的服务器),它以非阻塞模式运行良好。


-1
import threading

def hello():
   print "hello, world"
   t = threading.Timer(30.0, hello)
   t.start() # after 30 seconds, "hello, world" will be printed

我不太擅长Python,也没有尝试过这个。看看这是否能帮到你 :)


@hayden 这不是真的。通过将 threading.activeCount() 添加到 print 语句中,您可以看到最多有 3 个正在运行的线程(在 t.start() 之前有 2 个,在之后有 3 个):父线程(之前的 hello),当前正在执行的线程(当前的 hello)和正在生成的线程(t.start)。这段代码的问题在于无法跳出计时器循环,强制用户在外部终止进程。 - Nisan.H
@Nisan.H 这仍然是一个相当大的问题... - Andy Hayden
嗯,在每个步骤上添加一个条件检查以确定循环是否应该停止,对于一些线程安全的变量来说并不是非常困难。这是一个非常基本的计时器,通常情况下,我会避免在任何比单功能无限循环更复杂的情况下使用它,但它确实有效。当然,在这个例子中,也可以在while True:...循环中使用time.sleep(x)而不需要线程,因为它在这个例子中没有任何好处。 - Nisan.H
另外,使用外部的 threading.Timer(x, hello) 调用来启动它,可以让你继续运行主工作线程(例如脚本的其他部分),与递归生成/死亡的计时器线程分开。但在退出主工作线程之前,你将无法控制该线程。 - Nisan.H

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接