Python 2.7中的可线程延迟

2 投票
1 回答
504 浏览
提问于 2025-04-17 19:47

我现在在用Python(2.7)写一个图形界面程序,这个程序里有一些线程在运行。我遇到一个问题,就是我需要在获取一段信息之前大约延迟一秒,但我又不能让这个函数运行超过几毫秒。考虑到这一点,我想创建一个线程定时器,它会设置一个标志 timer.doneFlag,然后主函数会不断检查这个标志,看看是否完成。

这个方法是有效的,但并不是每次都能正常工作。我遇到的问题是,有时候我感觉 time.sleep 函数在 run 中,并没有完全等待一秒(有时候甚至根本不等待)。我只需要一个标志,能让我控制开始时间,并在达到一秒时把这个标志设为真。

我可能为了实现一个可线程化的延迟做得有点过了,如果你能给我一些建议,或者帮我找出以下代码中的错误,我会非常感激!

我附上了我使用的代码的一部分:

来自主程序:

class dataCollection:
    def __init__(self):
        self.timer=Timer(5)
        self.isTimerStarted=0  
        return

    def StateFunction(self): #Try to finish the function within a few milliseconds

        if self.isTimerStarted==0:
           self.timer=Timer(1.0)
           self.timer.start()
           self.isTimerStarted=1

        if self.timer.doneFlag:
           self.timer.doneFlag=0
           self.isTimerStarted=0
           #and all the other code



import time
import threading
class Timer(threading.Thread):          
    def __init__(self, seconds):            
        self.runTime = seconds          
        self.doneFlag=0
        threading.Thread.__init__(self)
    def run(self):      
        time.sleep(self.runTime)    
        self.doneFlag=1
        print "Buzzzz"

x=dataCollection()
while 1:
    x.StateFunction()
    time.sleep(0.1)

1 个回答

0

首先,你实际上是用更少的灵活性重新构建了threading.Timer。所以我觉得你还是直接使用现有的类比较好。(为每个定时器实例创建一个线程有一些明显的缺点。不过如果你只想要一个一次性的定时器,那这样做也没问题。)

更重要的是,让你的主线程不断地检查doneFlag可能不是个好主意。这意味着你必须尽可能频繁地调用你的状态函数,这样会浪费CPU资源,没什么实际意义。

你之所以需要在几毫秒内返回,可能是因为你要回到某种事件循环,可能是为了你的图形用户界面(不过,比如说,网络反应器也有同样的问题,解决方法也一样,所以我会保持内容的通用性)。

如果是这样,几乎所有这样的事件循环都有办法在事件循环内安排一个定时回调——在wx中是Timer,在twisted中是callLater,等等。所以,使用这些现有的功能吧。

如果你使用的框架没有类似的功能,希望至少有某种方式可以从外部发送事件/触发信号/发布消息/无论它叫什么。(如果是简单的基于文件描述符的反应器,可能没有这个功能,但你可以通过在反应器中添加一个管道来自己实现。)所以,把你的Timer回调改成信号事件循环,而不是写代码去轮询Timer

如果出于某种原因你真的需要轮询一个跨线程共享的变量,你真的应该用ConditionRLock来保护它。语言中没有保证,当线程0更新了值时,线程1会立即看到新值,甚至可能根本看不到。如果你对(某个特定版本的)CPython的内部机制了解得足够多,你可以证明在特定情况下GIL使得锁变得不必要。但在其他情况下,这就是一个竞争条件。

最后:

我遇到的问题是,有时候我觉得在运行中的time.sleep函数并没有完全等待一秒(有时候甚至可能不等待)。

那么,文档明确说明了这种情况可能发生:

实际的暂停时间可能少于请求的时间,因为任何捕获的信号会在执行该信号的捕获例程后终止sleep()。

所以,如果你需要保证它实际睡眠至少1秒,唯一的方法是像这样:

t0 = time.time()
dur = 1.0
while True:
    time.sleep(dur)
    t1 = time.time()
    dur = 1.0 - (t1 - t0)
    if dur <= 0:
        break

撰写回答