Модуль queue (встроенный в Python): при добавлении второго задания в очередь, оно сразу же начинает выполняться, но первое ещё не завершено

Я изучаю Python и фреймворк PyQt6 (а точнее PySide6).

Моя идея в том, что в основном потоке программы задания добавляются в очередь (модуль queue), а выполняются эти задания в одном отдельном потоке.

У меня получилось сделать простую программу и в ней задания выполняются по очереди, пока я добавляю задания, выполняется первое задание, после окончания первого задания начинает выполняться второе и тд, то есть все как я и хотел.

Но теперь я сделал программу немного посложнее и в ней так же queue
обрабатывается в отдельном потоке.
Но происходит странная вещь, с которой я никак не могу разобраться.
А именно - пока выполняется первое задание, я назначаю второе задание и оно сразу же начинает выполняться.
Причем оно выполняется с того же места, где было прервано первое задание.
При этом никаких ошибок и исключений не возникает.
Пришел к мысли, что проблема в потоке, который обрабатывает задания из очереди.

Программа, которую я пытаюсь реализовать, нужна для тестового стенда. На этот стенд устанавливается n-ное количество устройств одного вида, каждое в свой слот, при этом некоторые слоты могут оставаться пустыми.
С помощью программы выбирается нужный слот, выбираются необходимые тесты,
именно для этого слота и нажимается кнопка запуск.
Задание попадает в очередь, а программа начинает гонять тесты по первому устройству.
Далее для всех остальных устройств на стенде таким же образом назначаются тесты и отправляются в очередь при нажатии на кнопку запуск.
Стенд не может одновременно тестировать все устройства в слотах (из-за своего собственного устройства), а только по очереди, поэтому решил использовать очередь (модуль queue из Python) и отдельный поток (QThread из PySide6).

На данный момент грешу именно на часть кода в потоке, которая как раз получает и обрабатывает задания из очереди, что я там что-то неправильно сделал, так как остальные части этого механизма реализованы один в один с другой программой, в которой эта идея работает.
По крайней мере, мне так кажется.


UPD: Добавил минимальный воспроизводимый пример, заранее извиняюсь за длинный код, уменьшил как смог до 600 строк кода, некорректное поведение сохранилось.

При запуске программы открывается окно с 4-мя слотами, чтобы назначить проверки для слота, нужно сделать двойной клик на вертикальном прогресс-баре, тогда откроется окно с возможностью выбора проверок, можно выбрать тип устройства в комбобоксе (2 вида), после нажатия кнопки "Запуск" сразу же начнется проверка на выбранном слоте, при этом выбранные проверки будут "подсвечены" голубоватого цвета индикатором напротив назначенных проверок, текущая проверка - желтым цветом, пройденная - зеленым.

import sys, time
import queue
from PySide6.QtWidgets import (
    QApplication,
    QCheckBox,
    QComboBox,
    QLabel,
    QGridLayout,
    QMainWindow,
    QProgressBar,
    QPushButton,
    QHBoxLayout,
    QVBoxLayout,
    QWidget,
)
from PySide6.QtCore import Qt, Signal, QThread
from PySide6.QtGui import QMouseEvent

checks_list = [
    "Потребляемая мощность в рабочем режиме",  # 0
    "Потребляемая мощность в режиме КЗ",  # 1
    "Прогрев",  # 2
    "Опр-е основной приведенной погрешности",  # 3
    "Опр-е напр-я питания на холостом ходу",  # 4
    "Опр-е остаточного напряжения на датчике",  # 5
    "Определение падения напряжения на входе",  # 6
]

mibs_list = [
    "DEV-211",
    "DEV-212",
]


class MibWorker(QThread):

    # Сигнал для обновления цветовой метки Канала о состоянии: проверка пройдена успешно #зеленый
    update_slot_channel_label_pass = Signal(int)

    # Сигнал для обновления цветовой метки Канала о состоянии: проверка завершилась ошибкой #красный
    update_slot_channel_label_nopass = Signal(int)

    # Сигнал для обновления цветовой метки Канала о состоянии:
    # проверка проходится и ещё не завершена #желтый
    update_slot_channel_label_passes = Signal(int)

    # Сигнал для обновления поля - текущая проверка
    update_text_edit_signal = Signal(str)
    count_process = 0

    def __init__(self, queue):
        super().__init__()
        self.queue = queue

    def stopRunning(self):
        self.terminate()
        self.wait()
        self.count_process = 0
        self.update_text_edit_signal.emit(
            f"Этап {self.count_process} Проверка окончена"
        )

    def routine(self, num_check):
        process = 1
        y = 0
        while process == 1:
            for _ in range(5):
                y += 1
                time.sleep(1)
            process = 0
        if y == 5:
            self.update_slot_channel_label_pass.emit(num_check)
        else:
            self.update_slot_channel_label_nopass.emit(num_check)

    def powerConsumptionInOperatingMode(self, num_check):
        self.count_process += 1
        self.update_slot_channel_label_passes.emit(num_check)
        self.update_text_edit_signal.emit(
            f"Этап {self.count_process} Потребляемая мощность в рабочем режиме",
        )
        self.routine(num_check)

    def powerConsumptionInShortCircuitMode(self, num_check):
        self.count_process += 1
        self.update_slot_channel_label_passes.emit(num_check)
        self.update_text_edit_signal.emit(
            f"Этап {self.count_process} Потребляемая мощность в режиме КЗ",
        )
        self.routine(num_check)

    def warmingUp(self, num_check):
        self.count_process += 1
        self.update_slot_channel_label_passes.emit(num_check)
        self.update_text_edit_signal.emit(f"Этап {self.count_process} Прогрев")
        self.routine(num_check)

    def determinationOfTheMainReducedError(self, num_check):
        self.count_process += 1
        self.update_slot_channel_label_passes.emit(num_check)
        self.update_text_edit_signal.emit(
            f"Этап {self.count_process} Определение основной приведенной погрешности"
        )
        self.routine(num_check)

    def determinationOfTheSupplyVoltageAtIdle(self, num_check):
        self.count_process += 1
        self.update_slot_channel_label_passes.emit(num_check)
        self.update_text_edit_signal.emit(
            f"Этап {self.count_process} Определение напряжения питания на холостом ходу",
        )
        self.routine(num_check)

    def determinationOfTheResidualVoltageOnTheSensor(self, num_check):
        self.update_slot_channel_label_passes.emit(num_check)
        self.count_process += 1
        self.update_text_edit_signal.emit(
            f"Этап {self.count_process} Определение остаточного напряжения на датчике",
        )
        self.routine(num_check)

    def determinationOfTheVoltageDropAtTheInput(self, num_check):
        self.update_slot_channel_label_passes.emit(num_check)
        self.count_process += 1
        self.update_text_edit_signal.emit(
            f"Этап {self.count_process} Определение падения напряжения на входе",
        )
        self.routine(num_check)

    def checksList(self, task):
        if 0 in task:
            self.powerConsumptionInOperatingMode(0)
        if 1 in task:
            self.powerConsumptionInShortCircuitMode(1)
        if 2 in task:
            self.warmingUp(2)
        if 3 in task:
            self.determinationOfTheMainReducedError(3)
        if 4 in task:
            self.determinationOfTheSupplyVoltageAtIdle(4)
        if 5 in task:
            self.determinationOfTheResidualVoltageOnTheSensor(5)
        if 6 in task:
            self.determinationOfTheVoltageDropAtTheInput(6)

    def run(self):
        while True:
            task = self.queue.get()
            self.ch1_list = task[2][:]
            self.checksList(self.ch1_list)
            self.queue.task_done()
            self.ch1_list.clear()
            self.count_process = 0


class MibWindow(QWidget):

    mib_index_signal = Signal(list)
    mib_worker_stop_signal = Signal()
    mib_close_signal = Signal()

    mib_index = 0
    process_list_ch1 = []
    process_list_ch2 = []

    mib_channel_index = 0

    def __init__(self, num_slot=None, **kwargs):
        super().__init__(**kwargs)
        self.num_slot = num_slot
        self.initializeUI()

    def initializeUI(self):
        self.setWindowTitle("Окно назначения задания")
        self.setUpMibWindow()

    def setUpMibWindow(self):

        self.name_process = QLabel("Наименование проверки")
        self.name_process.setWordWrap(True)
        self.name_process.setAlignment(
            Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignCenter
        )

        # Формирование списка объектов - текстовых полей с наименованиями проверок
        self.slot_process_list = []
        self.slot_processes = [QLabel() for i in range(1, 7)]
        for index, process in enumerate(self.slot_processes):
            process.setMinimumHeight(20)
            process.setEnabled(False)
            process.setText(checks_list[index])
            self.slot_process_list.append(process)

        self.title_name_process = QLabel("Выбор проверки")
        self.title_name_process.setWordWrap(True)
        self.title_name_process.setAlignment(
            Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignCenter
        )

        self.title_channel_1 = QLabel("Канал 1")

        # Формирование списка объектов - чекбоксов для канала 1
        self.slot_cbox_ch1_list = []
        self.slot_ch1_cboxes = [QCheckBox() for i in range(1, 7)]
        for cbox in self.slot_ch1_cboxes:
            self.slot_cbox_ch1_list.append(cbox)

        self.all_check = QPushButton("Выбрать все")
        self.all_check.clicked.connect(self.allCheck)
        self.no_check = QPushButton("Снять все")
        self.no_check.clicked.connect(self.noCheck)

        bottom_box = QHBoxLayout()
        bottom_box.addWidget(self.all_check)
        bottom_box.addWidget(self.no_check)

        self.mib_list = [
            "MIB-211",
            "MIB-212",
        ]

        self.mib_combobox = QComboBox()
        self.mib_combobox.addItems(self.mib_list)
        self.mib_combobox.activated.connect(self.switchMib)

        self.launch_button = QPushButton("Запуск")
        self.launch_button.clicked.connect(self.launchUp)

        self.stop_button = QPushButton("Стоп")
        self.stop_button.clicked.connect(self.stopProcess)

        grid = QGridLayout()
        grid.setAlignment(Qt.AlignmentFlag.AlignTop)

        # 0-й столбец
        grid.addWidget(self.name_process, 0, 0, 1, 2)

        for process in self.slot_process_list:
            row = 2 + self.slot_process_list.index(process)
            grid.addWidget(process, row, 0, 1, 1)

        # 2-й столбец
        grid.addWidget(self.title_name_process, 0, 2, 1, 2)
        grid.addWidget(self.title_channel_1, 1, 2)

        for cbox in self.slot_cbox_ch1_list:
            row = 2 + self.slot_cbox_ch1_list.index(cbox)
            grid.addWidget(cbox, row, 2, 1, 1, Qt.AlignmentFlag.AlignCenter)

        # 4-й столбец
        grid.addWidget(self.mib_combobox, 1, 4)
        grid.addWidget(self.launch_button, 5, 4)
        grid.addWidget(self.stop_button, 6, 4)

        grid.addLayout(bottom_box, 16, 0, 1, 4, Qt.AlignmentFlag.AlignHCenter)
        grid.setColumnStretch(4, 10)

        self.setLayout(grid)

    def allCheck(self):
        for cbox in self.slot_cbox_ch1_list:
            cbox.setChecked(True)

    def noCheck(self):
        for cbox in self.slot_cbox_ch1_list:
            cbox.setChecked(False)

    def launchUp(self):

        self.process_list_ch1.clear()

        for cbox in self.slot_cbox_ch1_list:
            if cbox.isChecked():
                ind1 = self.slot_cbox_ch1_list.index(cbox)
                self.process_list_ch1.append(ind1)

        self.channels = 0

        self.check_list = []
        self.check_list.append(self.num_slot)
        self.check_list.append(self.mib_index)
        self.check_list.append(self.process_list_ch1)

        # Отправлен сигнал - задание в очередь (номер слота,
        # индекс устр-ва, список проверок К1)
        self.mib_index_signal.emit(self.check_list)

    def switchMib(self, index):
        self.mib_index = index
        self.noCheck()

    def stopProcess(self):
        # Сигнал для остановки проверок на слоте
        self.mib_worker_stop_signal.emit()


class MibSlot(QWidget):

    num_slot_signal = Signal(int)
    mib_slot_close_signal = Signal()
    mib_slot_signal = Signal(
        list,
    )
    mib_worker_stop_signal = Signal()

    def __init__(self, num_slot=None, **kwargs):
        super().__init__(**kwargs)
        self.num_slot = num_slot
        self.mib_window = MibWindow(self.num_slot)
        self.mib_window.mib_index_signal.connect(self.workMibSlot_1)
        self.mib_window.mib_close_signal.connect(self.closeMibWindow)
        self.mib_window.mib_worker_stop_signal.connect(self.stopProcess)
        self.initializeUI()

    def initializeUI(self):
        self.setUpMibSlot()

    def setUpMibSlot(self):

        # 1 - Слот №...
        self.slot_title_label = QLabel(f"Слот № {self.num_slot}")
        self.slot_title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)

        # 2 - Серийный номер устройства
        self.slot_serial_num_label = QLabel("Серийный номер:")

        # 3 - Тип устройства
        self.slot_device_type_label = QLabel("MIB")
        self.slot_device_type_label.setAlignment(Qt.AlignmentFlag.AlignCenter)

        # 4 - Прогресс-бар
        self.slot_pbar = QProgressBar()
        self.slot_pbar.setValue(0)
        self.slot_pbar.setOrientation(Qt.Orientation.Vertical)

        # 6 - Первый столбец индикаторов проверок
        # Заголовок столбца
        self.slot_name_channel_1_label = QLabel("Канал 1")
        self.slot_name_channel_1_label.setAlignment(Qt.AlignmentFlag.AlignCenter)

        # Формирование списка объектов - цветовых индикаторов проверок для канала 1
        self.slot_channel_1_label_list = []
        self.slot_channel_1_labels = [QLabel() for i in range(6)]
        for label in self.slot_channel_1_labels:
            label.setMaximumSize(10, 10)
            self.slot_channel_1_label_list.append(label)

        # 8 - Третий столбец - наименование проверки
        # Заголовок столбца
        self.names_checks_label = QLabel("Наименование проверки")
        self.names_checks_label.setAlignment(Qt.AlignmentFlag.AlignCenter)

        # Формирование списка объектов - текстовых полей с наименованиями проверок
        self.slot_name_check_label_list = []
        self.slot_name_check_labels = [QLabel() for i in range(6)]
        for index, label in enumerate(self.slot_name_check_labels):
            label.setText(checks_list[index])
            self.slot_name_check_label_list.append(label)

        # 9 - Оставшееся время до конца проверки
        self.slot_time_label = QLabel("Оставшееся время:")

        # 10 - Этап проверки (текст, какая идет проверка в данный момент)
        self.slot_step_label = QLabel("Текущая проверка:")

        grid_slot = QGridLayout()
        grid_slot.setVerticalSpacing(5)
        grid_slot.setContentsMargins(5, 5, 5, 5)

        # 0-й столбец
        grid_slot.addWidget(self.slot_title_label, 0, 0, 1, 12)
        grid_slot.addWidget(self.slot_serial_num_label, 1, 0, 1, 12)
        grid_slot.addWidget(self.slot_device_type_label, 2, 0, 1, 5)
        grid_slot.addWidget(self.slot_pbar, 3, 0, 14, 2)
        grid_slot.addWidget(self.slot_time_label, 17, 0, 1, 12)
        grid_slot.addWidget(self.slot_step_label, 18, 0, 1, 12)

        # 5-й столбец
        grid_slot.addWidget(self.slot_name_channel_1_label, 2, 5, 1, 3)

        # 6-й столбец
        for label in self.slot_channel_1_label_list:
            row = 3 + self.slot_channel_1_label_list.index(label)
            grid_slot.addWidget(label, row, 6, 1, 1)

        # 11-й столбец
        grid_slot.addWidget(self.names_checks_label, 2, 11, 1, 1)

        for label in self.slot_name_check_label_list:
            row = 3 + self.slot_name_check_label_list.index(label)
            grid_slot.addWidget(label, row, 11, 1, 1)

        self.setLayout(grid_slot)

        self.num_slot_signal.emit(self.num_slot)

    def eventFilter(self, obj, event):

        if (
            isinstance(obj, QProgressBar)
            and event.type() == QMouseEvent.Type.MouseButtonDblClick
        ):
            if self.mib_window.isVisible():
                self.mib_window.hide()
            else:
                if not self.mib_window.isVisible():
                    self.mib_window.show()
                else:
                    self.mib_window.close()
                    self.mib_window = MibWindow()
                    self.mib_window.mib_index_signal.connect(self.workMibSlot_1)
                    self.mib_window.mib_close_signal.connect(self.closeMibWindow)
                    self.mib_window.show()
        return QWidget.eventFilter(self, obj, event)

    def workMibSlot_1(self, check_list):
        self.mib_slot_signal.emit(check_list)

    def closeMibWindow(self):
        self.mib_slot_close_signal.emit()

    def stopProcess(self):
        self.mib_worker_stop_signal.emit()


class BasicWindow(QMainWindow):

    window_close_signal = Signal()
    window_ignore_signal = Signal()

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.queue = queue.Queue()
        self.worker_mib = MibWorker(self.queue)
        self.process_list_ch1 = []
        self.process_list_ch2 = []
        self.initializeUI()

    def initializeUI(self):
        self.setWindowTitle("Главное окно программы")
        self.setUpBasicWindow()
        self.show()

    def setUpBasicWindow(self):

        # Формирование списка объектов - слотов для главного окна
        self.slot_mib_list = []
        self.slot_list = [MibSlot(i) for i in range(1, 5)]
        for mib in self.slot_list:
            mib.setObjectName("Slot")
            mib.mib_slot_signal.connect(self.workMibSlot_1)
            mib.mib_slot_close_signal.connect(self.closeMibWindow)
            mib.slot_pbar.installEventFilter(mib)
            self.slot_mib_list.append(mib)

        self.slot_h_box_list = []
        h_boxes = [QHBoxLayout() for i in range(4)]
        for index, box in enumerate(h_boxes):
            box.addWidget(self.slot_mib_list[index])
            self.slot_h_box_list.append(box)

        self.slot_widget_list = []
        widgets = [QWidget() for i in range(4)]
        for index, widget in enumerate(widgets):
            widget.setLayout(self.slot_h_box_list[index])
            self.slot_widget_list.append(widget)

        # Макет расположения слотов
        grid = QGridLayout()
        for row in range(2):
            if row == 0:
                for col in range(2):
                    grid.addWidget(self.slot_widget_list[col], row, col, 1, 1)
            elif row == 1:
                for col in range(2):
                    grid.addWidget(self.slot_widget_list[col + 2], row, col, 1, 1)

        # Создание поля рабочего пространства
        self.workspace = QWidget()
        self.workspace.setLayout(grid)

        main_v_box = QVBoxLayout()
        main_v_box.addWidget(self.workspace)

        # Установка основного макета окна
        container = QWidget()
        container.setLayout(main_v_box)
        self.setCentralWidget(container)

    def worker_thread(self):
        self.worker_mib.update_text_edit_signal.connect(self.updateTextEdit)
        self.worker_mib.update_slot_channel_label_pass.connect(self.processPass)
        self.worker_mib.update_slot_channel_label_nopass.connect(self.processNoPass)
        self.worker_mib.update_slot_channel_label_passes.connect(self.processPasses)
        self.slot_mib_list[self.num_slot].mib_worker_stop_signal.connect(
            self.worker_mib.stopRunning
        )

        self.worker_mib.finished.connect(self.updateTextStep)
        self.worker_mib.start()

    def workMibSlot_1(self, check_list):
        self.num_slot = check_list[0] - 1
        self.index_mib = check_list[1]
        self.check_list = check_list[2]

        self.slot_mib_list[self.num_slot].slot_device_type_label.setText(
            mibs_list[self.index_mib]
        )
        self.scheduledOneOff()
        self.scheduledOne()

        self.queue.put(check_list)
        self.worker_thread()

    def closeMibWindow(self):
        if self.worker_mib:
            self.slot_mib_list[self.num_slot].mib_window.hide()
        else:
            self.slot_mib_list[self.num_slot].mib_window.close()

    def scheduledOne(self):
        # подсвечивает индикаторы проверок
        # в соответствии со списком выбранных проверок
        for proc in self.check_list:
            for index, name in enumerate(
                self.slot_mib_list[self.num_slot].slot_name_check_label_list
            ):
                if proc == index:
                    self.slot_mib_list[self.num_slot].slot_channel_1_label_list[
                        index
                    ].setStyleSheet(
                        "border: 1px solid black; border-radius: 2px; background-color: #55ffff"
                    )

    def processPass(self, process):
        # Если проверка пройдена успешно
        # зеленый
        self.slot_mib_list[self.num_slot].slot_channel_1_label_list[
            process
        ].setStyleSheet(
            "border: 1px solid black; border-radius: 2px; background-color: #33ff00"
        )

    def processNoPass(self, process):
        # Если проверка пройдена не успешно
        # красный
        self.slot_mib_list[self.num_slot].slot_channel_1_label_list[
            process
        ].setStyleSheet(
            "border: 1px solid black; border-radius: 2px; background-color: #ff0000"
        )

    def processPasses(self, process):
        # Если проверка в процессе прохождения
        # желтый
        self.slot_mib_list[self.num_slot].slot_channel_1_label_list[
            process
        ].setStyleSheet(
            "border: 1px solid black; border-radius: 2px; background-color: #ffff00"
        )

    def updateTextStep(self):
        self.slot_mib_list[self.num_slot].slot_step_label.setText(
            f"Этап 0 Проверка окончена"
        )

    def updateProgressBar(self, value):
        self.slot_mib_list[self.num_slot].slot_pbar.setValue(value)

    def updateTextEdit(self, text):
        if text != "":
            self.slot_mib_list[self.num_slot].slot_step_label.setText(text)

    def updateTimeEdit(self, time):
        self.slot_mib_list[self.num_slot].slot_time_label.setText(f"00:{time}")

    def scheduledOneOff(self):
        for label in self.slot_mib_list[self.num_slot].slot_name_check_label_list:
            label.setStyleSheet("color: grey")
        for label in self.slot_mib_list[self.num_slot].slot_channel_1_label_list:
            label.setStyleSheet(
                "border: 1px solid grey; border-radius: 2px; background-color: #f4f4f4"
            )

    def closeEvent(self, event):
        if event:
            if self.worker_mib.isRunning():
                self.worker_mib.stopRunning()


# Запустить программу
if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = BasicWindow()
    sys.exit(app.exec())

Главное окно и окно выбора проверок

UPD2: в потоке нужно было прописывать сами проверки, а не вызывать их оттуда как функции.


Ответы (1 шт):

Автор решения: SVBazuev

Минимально воспроизводимый пример выглядит так:

import re
import sys
import time
import queue
from PySide6.QtWidgets import (
    QApplication, QMainWindow, QPushButton, QVBoxLayout,
    QWidget, QListWidget, QListWidgetItem, QProgressBar
)
from PySide6.QtCore import QThread, Signal

class WorkerThread(QThread):
    # Передаем индекс задачи и ее состояние
    update_signal = Signal(int, str)  
    # Передаем прогресс выполнения
    progress_signal = Signal(int)  
    
    def __init__(self, task_queue):
        super().__init__()
        self.task_queue = task_queue
        self._running = True  # Флаг для управления потоком

    def run(self):
        while self._running:
            try:
                # Получаем задачу из очереди
                task_id = self.task_queue.get(timeout=1)
                # Обновляем статус  
                self.update_signal.emit(task_id, "In progress") 

                # Имитация длительной задачи 
                for i in range(100):  
                    time.sleep(0.05)  # Имитация работы
                    # Обновляем прогресс
                    self.progress_signal.emit(i + 1)

                # Обновляем статус  
                self.update_signal.emit(task_id, "Completed")  
                self.task_queue.task_done()
            except queue.Empty:
                continue  # Если очередь пуста, продолжаем ожидать

    def stop(self):
        self._running = False  # Остановка потока


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Queue with QThread Example")
        self.task_queue = queue.Queue()
        self.count_tasks = self.counter()
        # Передаём очередь как аргумент
        self.worker_thread = WorkerThread(self.task_queue)    
        #  Тут настраиваем коннект между потоками
        self.worker_thread.update_signal.connect(self.update_status)
        #  Чтоб прогресс обработки был виден в основном потоке
        self.worker_thread.progress_signal.connect(self.update_progress)
        layout = QVBoxLayout()
        self.task_list_widget = QListWidget()
        layout.addWidget(self.task_list_widget)
        self.progress_bar = QProgressBar()
        self.progress_bar.setRange(0, 100)
        layout.addWidget(self.progress_bar)
        self.button = QPushButton("Добавить задачу")
        self.button.clicked.connect(self.add_task)
        layout.addWidget(self.button)
        container = QWidget()
        container.setLayout(layout)
        self.setCentralWidget(container)
        self.worker_thread.start()  # Запускаем поток обработки

    def add_task(self):
        task_id = self.count_tasks()  # Получаем новый ID задачи
        self.task_queue.put(task_id)  # Добавляем задачу в очередь
        item = QListWidgetItem()  # Создаем элемент
        # Сохраняем ID задачи и стартовый статус
        item.setData(0, f"Задача {task_id}: Waiting process")
        self.task_list_widget.addItem(item)  # Добавляем элемент в список
        self.progress_bar.setValue(0)

    def counter(self):
        count = 0
        def inner():
            nonlocal count
            count += 1
            return count
        return inner

    def update_status(self, task_id, status):
        item = self.task_list_widget.item(task_id-1)
        item.setText(f"Задача {task_id}: {status}")

    def update_progress(self, value):
        self.progress_bar.setValue(value)

    def closeEvent(self, event):
        self.worker_thread.stop()  # Останавливаем поток при закрытии окна
        self.worker_thread.wait()  # Ждем завершения потока
        event.accept()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

^^^ В решени есть очередь и обработка в отдельном потоке.


Я не пытался воспроизвести Вашу проблему,
у меня всё работает:

введите сюда описание изображения


UPD: У Вас нет никакой проблемы с очередями и потоками.
У Вас проблемы с архитектурой, отсутствие абстракции функционального программирования (бусы однотипных методов в MibWorker), нарушение инкапсуляции в MibSlot (всё что относится к классу должно быть в нём). Например, check_list однозначно должен быть атрибутом экземпляра, и методы обработки сигналов тоже. А в очередь надо добавлять самодостаточный объект self.queue.put(slot1 := MibSlot(1)) - выглядеть это будет немного иначе, т.к. слоты инициализированы при запуске приложения.
И с сигналами надо отправлять идентифицирующие аргументы...
Если у Вас виджет принадлежит одному объекту,
а методы изменяющие его состояние почему-то в другом классе ... ⁉️

Ваш код надо переделывать.

Успехов!

→ Ссылка