【问题标题】:DeadLock in PythonPython中的死锁
【发布时间】:2018-03-07 23:29:47
【问题描述】:

我正在用tkinter 编写一个GUI,它使用cv2 从计算机摄像头中提取视频流。为此,Thread 使用 while 循环重新加载图像。

我一直在使用Locks,所以Threads(主要和辅助)都按预期结束了他们的进程。但是,由于我无法识别的死锁,我无法做到这一点。

代码如下:

import tkinter as tk
import cv2
import time
import sys
import threading
from PIL import ImageTk, Image


class Application:
    gui_root = None
    gui_panel = None

    frame = None
    video_capture = None
    stop_event = None
    thread = None
    thread_lock = None

    def __init__(self):

        self.gui_root = tk.Tk()
        self.gui_root.geometry("640x480")
        self.gui_root.resizable(False, False)

        self.video_capture = cv2.VideoCapture(0)

        self.stop_event = threading.Event()
        self.thread_lock = threading.Lock()
        self.thread = threading.Thread(target=self.load_frame, args=())
        self.thread.start()

        self.gui_root.wm_protocol("WM_DELETE_WINDOW", self.on_close)

    def start(self):

        self.gui_root.mainloop()

    def load_frame(self):

        print('Starting load_frame')

        while self.thread_lock.acquire() and not self.stop_event.is_set():

                _, self.frame = self.video_capture.read()
                self.frame = cv2.resize(self.frame, (640, 480))

                c_image = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGB)
                c_image_tk = ImageTk.PhotoImage(Image.fromarray(c_image))

                if self.gui_panel is None:
                    self.gui_panel = tk.Label(image=c_image_tk)
                    self.gui_panel.place(width=640, height=480, x=0, y=0)
                else:
                    self.gui_panel.configure(image=c_image_tk)

                self.thread_lock.release()

        print("End of while")
        self.thread_lock.release()
        print('End of Process 2')

    def on_close(self):

        self.stop_event.set()
        print("Event set")
        with self.thread_lock:
            self.video_capture.release()
            self.gui_root.destroy()


app = Application()
app.start()

【问题讨论】:

  • 一般来说,你只能在主线程上做 Tk 的东西。如果您执行诸如在工作线程上创建新标签之类的操作,具体取决于您的平台、代码的确切细节、是否是星期二等,它可能会工作、什么也不做、崩溃、挂起或挂起,直到主线程得到运行事件循环的机会。最后一个会导致字面上的死锁,如果你不是很精确,其他一些可能看起来像死锁。

标签: python multithreading tkinter locking deadlock


【解决方案1】:

这里最可能的问题是,您正在尝试从后台线程执行 Tkinter 的操作,例如创建一个新的 tk.Label,这是不允许的。

我的第一个猜测是,后台线程上的 Tk 代码只是挂起——这并不是真正的死锁,但由于你有一个线程在持有锁的同时永远挂起,所以没有人会永远获取那个锁,所以效果差不多。

解决此问题的唯一方法是让您的后台线程将消息发布到队列,请求 GUI 内容为它们完成,并让主线程每次通过事件循环轮询该队列(例如,通过after 函数)。

旧的mttkinter 项目透明地包装了它,但不幸的是它从未针对现代 Python 进行更新。几年前,我开发了一个快速端口(@98​​7654321@),但我从未对其进行严格测试,也不知道是否有人将它用于任何严重的事情。

但您至少可以尝试一下以进行快速测试。如果没有区别,那么您可能已经排除了 Tk 线程问题;如果它有所作为,那就太好了(尽管这种区别可能是“现在它不会冻结,但它有 50% 的时间会崩溃——在这种情况下,你还没有完成,并且不能信任 mttkinter as-是,但至少你知道 Tk 线程是你必须解决的问题)。

【讨论】:

  • 感谢您的回答。花了很多时间试图让tkinter 工作和你的回答,我终于决定直接使用 OpenCV。
猜你喜欢
  • 1970-01-01
  • 2013-07-02
  • 1970-01-01
  • 2012-03-03
  • 1970-01-01
  • 2014-08-26
  • 2020-06-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多