簡體   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