python - 如何在 PyQt 中的另一个线程中暂停线程中的循环
问题描述
我正在尝试编写一个使用两个工作人员的简单 PyQt 小部件。第一个工人(工人 1)立即开始其循环,而第二个工人(工人 2)睡眠 2 秒然后开始。我想得到以下信息:
- Worker 2 暂停 Worker 1 的执行
- Worker 2 启动并完成循环
- Worker 2 完成并让 Worker 1 完成自己的循环。
我试图通过为工人类实现以下代码来实现这一点:
class Worker(QObject):
"""
Create worker class
"""
worker_finished = pyqtSignal()
ask_to_work = pyqtSignal()
glob_lock = QMutex()
def __init__(self, name, sleep_time = 0.5, loops = 10, sleep_at_start=False):
super().__init__()
self._name = name
self.sleep_time = sleep_time
self.loops = loops
self._sleep_at_start = sleep_at_start
self.worker_finished.connect(self.at_finish)
self.ask_to_work.connect(self.pause_execution)
@pyqtSlot()
def run(self):
#This is to make the worker sleep 2 seconds, then ask to run its loop
if self._sleep_at_start:
print("{:s} is sleeping.".format(self._name))
sleep(2)
print("{:s} has awaken.".format(self._name))
self.ask_to_work.emit()
#This acquires the lock and starts the worker loop
self.glob_lock.lock()
print("Lock acquired by {:s}".format(self._name))
for ii in range(self.loops):
print("I am {:s} and I am at step {:d}".format(self._name, ii))
sleep(self.sleep_time)
self.worker_finished.emit()
#Function to attempt the interruption of the worker loop
@pyqtSlot()
def pause_execution(self):
try:
print("Request lock")
self.glob_lock.unlock()
print("Request succeded")
except:
print("No lock to release")
@pyqtSlot()
def at_finish(self):
try:
self.glob_lock.unlock()
print("Successfully released lock at finish.")
except:
print("Releasing the lock at finish went wrong.")
而小部件和执行是:
class MainApp(QWidget):
"""
Make the widget
"""
def __init__(self):
super().__init__()
#Set the widget layout
layout = QHBoxLayout(self)
self.button_run = QPushButton("Run workers")
layout.addWidget(self.button_run)
self._threads = []
self._workers = []
#Initialize the workers
worker = Worker("Worker 1", loops=10)
self._workers.append(worker)
worker = Worker("Worker 2", sleep_time=1,
loops=1, sleep_at_start=True)
self._workers.append(worker)
#Connect workers' run methods to the widget button and put the workers into separate threads
self.activate_and_connect_workers()
def activate_and_connect_workers(self):
for worker in self._workers:
self.button_run.clicked.connect(worker.run)
a_thread = QThread()
self._threads.append(a_thread)
worker.moveToThread(a_thread)
a_thread.start()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainApp()
window.show()
sys.exit(app.exec_())
我想在终端看到的是:
Lock acquired by Worker 1
Worker 2 is sleeping
I am Worker 1 and I am at step 0
I am Worker 1 and I am at step 1
#...Some lines of Worker 1
Worker 2 has awaken.
Request lock
Request succeded
Lock acquired by Worker 2
I am Worker 2 and I am at step 0
#...Some lines of Worker 2
Worker 2 uccessfully released lock at finish.
Lock acquired by Worker 1
#...More lines of Worker 1
Worker 1 uccessfully released lock at finish.
这不会发生,因为 Worker 2 发出了“对自己”释放锁的信号,并且无法将其发送给 Worker 1。这意味着两个进程不同步。最重要的是,程序在 Worker 1 循环结束时崩溃(可能是因为在 Worker 1 重新启动时锁已经被释放)。
我应该怎么做才能使代码以所需的方式工作?
解决方案
该文件指出:
当您在一个线程中调用 lock() 时,其他尝试在同一位置调用 lock() 的线程将阻塞,直到获得锁的线程调用 unlock()。
因此,唯一允许解锁的线程是 Worker1 的线程。
根据您对要实现的目标的描述,我认为 lock() 是错误的方法。锁定“保护”一段代码,因此一次只有一个线程可以访问它。
如果您仍然想使用这种阻塞机制,我建议使用以下模式:
添加一个“is_blocking”标志,确定工作人员是否应该在其生命周期内获得锁。如果工作人员没有阻塞,它会定期尝试锁定和解锁。如果阻塞的 Worker 处于活动状态,则会导致阻塞行为。
这是一个粗略的演示,基于您的示例:
import sys
from PyQt5.QtCore import QObject, pyqtSignal, QMutex, pyqtSlot, QThread, Qt
from PyQt5.QtWidgets import QWidget, QHBoxLayout, QPushButton, QApplication
from time import sleep
glob_lock = QMutex()
class Worker(QObject):
worker_finished = pyqtSignal()
def __init__(self, name, sleep_time = 0.5, loops = 10, sleep_at_start=False, is_blocking=False):
super().__init__()
self._name = name
self.sleep_time = sleep_time
self.loops = loops
self._sleep_at_start = sleep_at_start
self.worker_finished.connect(self.at_finish)
self.is_blocking = is_blocking
@pyqtSlot()
def run(self):
if self._sleep_at_start:
print("{:s} is sleeping.".format(self._name))
sleep(2)
print("{:s} has awaken.".format(self._name))
if self.is_blocking:
glob_lock.lock()
print("Lock acquired by {:s}".format(self._name))
for ii in range(self.loops):
if not self.is_blocking:
glob_lock.lock()
glob_lock.unlock()
print("I am {:s} and I am at step {:d}".format(self._name, ii))
sleep(self.sleep_time)
self.worker_finished.emit()
@pyqtSlot()
def at_finish(self):
if self.is_blocking:
glob_lock.unlock()
print("Successfully released lock at finish.")
else:
print("Successfully finished.")
class MainApp(QWidget):
def __init__(self):
super().__init__()
layout = QHBoxLayout(self)
self.button_run = QPushButton("Run workers")
layout.addWidget(self.button_run)
self._threads = []
self._workers = []
worker = Worker("Worker 1", loops=10)
self._workers.append(worker)
worker = Worker("Worker 2", sleep_time=1, loops=5, sleep_at_start=True, is_blocking=True)
self._workers.append(worker)
self.activate_and_connect_workers()
def activate_and_connect_workers(self):
for worker in self._workers:
self.button_run.clicked.connect(worker.run)
a_thread = QThread()
self._threads.append(a_thread)
worker.moveToThread(a_thread)
a_thread.start()
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainApp()
window.show()
sys.exit(app.exec_())
推荐阅读
- c - 用 C 重命名 .mp4 列表
- complex-numbers - 小波变换和余弦变换之间是否存在关系
- ios - 快速解析 JSON 时出现 typeMismatch 错误
- google-bigquery - bigquery中的多个array_agg
- javascript - 为什么我在获取数据表单 API 时变得不确定
- python-3.x - 此代码未在 python 文件上运行,但表示它以正确的hackerran 运行
- python - 如何获取出现在cmd上的用户名?
- gdb - 无法使用 MinGW 交叉编译 gdb
- r - 如果列超过 R Markdown 中的页面容量,如何从表中拆分行?
- javascript - 即使没有任何错误,我的 discord.js 机器人也不会回复用户消息