【问题标题】:class attribute doesn't get assigned correctly类属性未正确分配
【发布时间】:2017-05-15 13:59:14
【问题描述】:

我正在尝试循环警报(在文件 beep.wav 中)并显示警报。

警报关闭后,我想立即停止警报。

我正在尝试使用线程控制警报播放的解决方案。

但是,它会引发错误:

Traceback (most recent call last):
  File "test.py", line 28, in <module>
    thread.stop()
  File "test.py", line 21, in stop
    self.process.kill()
AttributeError: 'AlarmThread' object has no attribute 'process'

我真的不知道为什么会抛出这个错误,但看起来 self.process 出于某种原因在调用 AlarmThread.stop 时没有分配。

这对我来说毫无意义,因为从我的代码看来 thread.stop 仅在 thread.start 之后被调用:

import subprocess
import threading

class AlarmThread(threading.Thread):
    def __init__(self, file_name="beep.wav"):
        super(AlarmThread, self).__init__()
        self.file_name = file_name
        self.ongoing = None

    def run(self):
        self.ongoing = True
        while self.ongoing:
            self.process = subprocess.Popen(["afplay", self.file_name])
            self.process.wait()

    def stop(self):
        if self.ongoing is not None:
            self.ongoing = False
            self.process.kill()

thread = AlarmThread()
thread.start()
# show_alert is synchronous, an alert must be closed before the script continues
show_alert("1 second timer")

thread.stop()
thread.join()

【问题讨论】:

    标签: python multithreading macos subprocess python-2.x


    【解决方案1】:

    你有一个竞争条件。线程没有时间启动,创建进程并在您调用thread.stop() 时分配self.process。您可以在 __init__ 中初始化 self.process 并使用它来查看进程是否真的存在

    import subprocess
    import threading
    
    class AlarmThread(threading.Thread):
        def __init__(self, file_name="beep.wav"):
            super(AlarmThread, self).__init__()
            self.lock = threading.Lock()
            self.file_name = file_name
            self.ongoing = False
            self.process = None
    
        def run(self):
            self.ongoing = True
            while True:
                with self.lock:
                    if not self.ongoing:
                        break
                    self.process = subprocess.Popen(["afplayer", self.file_name])
                self.process.wait()
    
        def stop(self):
            with self.lock:
                if self.ongoing:
                    self.ongoing = False
                    if self.process:
                        self.process.kill()
    
    
    thread = AlarmThread()
    thread.start()
    # show_alert is synchronous, an alert must be closed before the script continues
    show_alert("1 second timer")
    
    thread.stop()
    thread.join()
    

    【讨论】:

    • 应该if self.processwhile not self.process: pass 等到线程准备好被杀死?
    • @theonlygusti - 我加了一些锁来堵住这些洞。它在我编写的一些黑客测试中起作用。您可以在您的环境中进行测试吗?
    • 与我自己的解决方案(下)相比,使用锁有什么好处吗?
    • 是的。考虑self.process = subprocess.Popen(["afplayer", self.file_name])。 Popen 需要大量时间,当另一个线程检查self.process == None 时,您可能正处于该调用的中间。在检查时它的 None ......但随后被分配。检查self.ongoing 和之后采取的操作之间的差距也存在类似问题。
    • 我试图在检查变量的顺序上变得聪明,但在第一个非锁定版本中仍然存在问题。
    【解决方案2】:

    是的,它是由竞争条件引起的。

    线程还没来得及启动,创建进程并在你调用thread.stop()时分配self.process

    但是,我发现了一个修复方法,它依赖于等待分配 thread.process

    thread = AlarmThread()
    thread.start()
    while not thread.process:
        time.sleep(0.1)
    
    show_alert(message)
    
    thread.stop()
    thread.join()
    

    我的班级也略有改变,以确保始终分配thread.process

    class AlarmThread(threading.Thread):
        def __init__(self, file_name="beep.wav"):
            super(AlarmThread, self).__init__()
            self.file_name = file_name
            self.ongoing = None
            self.process = None
    
        def run(self):
            self.ongoing = True
            while self.ongoing:
                self.process = subprocess.Popen(["afplay", self.file_name])
                self.process.wait()
                self.process = None
    
        def stop(self):
            if self.ongoing is not None:
                self.ongoing = False
                self.process.kill()
    

    【讨论】:

    • 查看run,在设置self.ongoing 和实际分配self.process 之间存在一个窗口(例如,执行“afplay”需要大量时间)。因为self.process = None,每个循环都存在这个差距。此外,self.ongoing 应该只是 True/False ...您将其设置为 False 但检查 None
    猜你喜欢
    • 2011-03-05
    • 2019-08-15
    • 1970-01-01
    • 2019-09-10
    • 1970-01-01
    • 1970-01-01
    • 2018-10-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多