繁体   English   中英

如何在 PyQt 中使用另一个线程暂停线程中的循环

[英]How to pause loop in thread with another thread in PyQt

我正在尝试编写一个使用两个工人的简单 PyQt 小部件。 第一个工作人员(工作人员 1)立即开始循环,而第二个工作人员(工作人员 2)休眠 2 秒然后开始。 我想得到以下内容:

  1. Worker 2 暂停 Worker 1 的执行
  2. Worker 2 启动并完成循环
  3. Worker 2 完成并让 Worker 1 完成自己的循环。

我试图通过为 worker 类实现以下代码来实现这一点:

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 没有阻塞,它会定期尝试锁定和解锁。 如果阻塞 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_())

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM