python 主线程_阻止主线程直到python后台线程完成side-task

该博客围绕Python线程应用程序展开,后台线程有长时间运行的mainloop,调用pyglet.app.run()驱动GUI窗口。需从主线程调用do_stuff函数触发GUI动画,等待动画停止后返回。给出了相关代码示例,还探讨了同步对象和策略的选择问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我有一个线程python应用程序,后台线程中有一个长时间运行的mainloop.这个后台主循环实际上是对pyglet.app.run()的调用,它驱动一个GUI窗口,也可以配置为定期调用其他代码.我需要从主线程中随意调用do_stuff(duration)函数来触发GUI中的动画,等待动画停止,然后返回.实际动画必须在后台线程中完成,因为GUI库无法处理由单独的线程驱动.

我相信我需要做这样的事情:

import threading

class StuffDoer(threading.Thread):

def __init__(self):

threading.Thread.__init__(self)

self.max_n_times = 0

self.total_n_times = 0

self.paused_ev = threading.Event()

def run(self):

# this part is outside of my control

while True:

self._do_stuff()

# do other stuff

def _do_stuff(self):

# this part is under my control

if self.paused_ev.is_set():

if self.max_n_times > self.total_n_times:

self.paused_ev.clear()

else:

if self.total_n_times >= self.max_n_times:

self.paused_ev.set()

if not self.paused_ev.is_set():

# do stuff that must execute in the background thread

self.total_n_times += 1

sd = StuffDoer()

sd.start()

def do_stuff(n_times):

sd.max_n_times += n_times

sd.paused_ev.wait_for_clear() # wait_for_clear() does not exist

sd.paused_ev.wait()

assert (sd.total_n_times == sd.max_n_times)

编辑:使用max_n_times而不是stop_time来阐明为什么Thread.join(持续时间)不会这样做.

wait([timeout])

Block until the internal flag is true.

If the internal flag is true on entry,

return immediately. Otherwise, block

until another thread calls set() to

set the flag to true, or until the

optional timeout occurs.

我发现如果我有一对事件paused_ev和not_paused_ev,并且使用not_paused_ev.wait(),我可以得到我正在寻找的行为.我几乎只能使用Thread.join(持续时间),除非它只需要在后台线程实际注册时间到时才精确返回.我应该使用其他同步对象或其他策略吗?

我也愿意接受这样的论点,即如果他们是一个好的论据,那么我正以错误的方式接近这一切.

### Python GUI 使用 `time.sleep` 导致界面冻结的解决方案 #### 对于 Tkinter 的解决方案 在 Tkinter 中,由于整个框架是单线程的,在主线程中调用阻塞操作(如 `time.sleep()`)会使图形用户界面 (GUI) 失去响应。为了防止这种情况发生,可以采用多线程技术或将长时间运行的任务分割成多个小部分并利用 `after` 方法。 通过创建一个新的线程来执行耗时的操作,可以让主事件循环继续处理用户的输入和其他任务而不会被阻止。下面是一个简单的例子展示如何实现这一点: ```python import threading import tkinter as tk from tkinter import messagebox def long_running_task(): # 这里模拟了一个耗时较长的任务 for i in range(5): print(f"Working... {i}") time.sleep(1) root.after(0, lambda: messagebox.showinfo("Info", "Task completed")) root = tk.Tk() button = tk.Button(root, text="Start Long Task", command=lambda: threading.Thread(target=long_running_task).start()) button.pack() root.mainloop() ``` 另一种方式是在不引入额外线程的情况下使用 `after` 函数定期更新UI状态,从而避免直接调用 `sleep`: ```python import tkinter as tk class App(tk.Frame): def __init__(self, master=None): super().__init__(master) self.master = master self.pack() self.create_widgets() self.counter = 0 def create_widgets(self): self.start_button = tk.Button(self) self.start_button["text"] = "Start" self.start_button["command"] = self.long_operation self.start_button.pack(side="top") def long_operation(self): if self.counter < 5: print(f"Working...{self.counter}") self.counter += 1 self.after(1000, self.long_operation) # 延迟一秒再次调用此方法 else: messagebox.showinfo("Info", "Task Completed") if __name__ == "__main__": root = tk.Tk() app = App(master=root) app.mainloop() ``` #### 对于 PyQt 的解决方案 对于 PyQt 来说,同样存在类似的挑战。为了避免因调用了像 `time.sleep()` 这样的阻塞性函数而导致应用程序卡顿的现象,应该考虑将这些操作放在单独的工作线程完成。这里给出一个基于 QThread 类的例子[^1]: ```python from PyQt5.QtCore import Qt, QThread, pyqtSignal from PyQt5.QtWidgets import QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QWidget import sys import time class Worker(QThread): finished = pyqtSignal(str) def run(self): for i in range(5): print(f'Processing... {i}') time.sleep(1) self.finished.emit('Operation Complete') class MainWindow(QMainWindow): def __init__(self): super().__init__() layout = QVBoxLayout() self.label = QLabel("Press the button to start.") layout.addWidget(self.label) btn = QPushButton("Start Processing") btn.pressed.connect(self.on_start_processing) layout.addWidget(btn) container = QWidget() container.setLayout(layout) self.setCentralWidget(container) def on_start_processing(self): worker_thread = Worker() worker_thread.finished.connect(self.update_label_text) worker_thread.start() def update_label_text(self, message): self.label.setText(message) app = QApplication(sys.argv) window = MainWindow() window.show() app.exec_() ``` 上述代码展示了两种不同的策略用于解决 Python GUI 应用程序中的界面冻结问题——无论是使用 Tkinter 还是 PyQt ——都强调了异步编程的重要性以及适当分离计算密集型工作的必要性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值