【问题标题】:How to stop the execution of a thread如何停止线程的执行
【发布时间】:2019-12-29 07:08:26
【问题描述】:

在下面的代码中,我有一个类 The Thread,它继承自 threading 以在不同的线程中执行任务,在这种情况下,任务是在 QLabel 中显示 .gif 图像,同时执行另一个任务。

在这种情况下,当发出执行 Finish() 函数的信号时,tabWidget() 小部件从 index 变为 1

但是当我们返回索引 0 时,我们可以看到 .gif 仍在 QLabel 中运行。

所以我想知道如何停止该线程的执行, 我试过了

TheThread.kill()
TheThread.stop()

但它不起作用,这是带有.ui文件和image.gif的完整代码

from PyQt5.QtWidgets import QMainWindow,QApplication,QTabWidget,QPushButton,QLabel
from PyQt5 import QtCore,QtGui
from PyQt5 import uic
import threading
import time

class TheThread(threading.Thread):
    def __init__(self,obj,fun):
        threading.Thread.__init__(self)

        self.obj = obj
        self.fun = fun

    def run(self):
        self.fun()
class Main(QMainWindow):
    signal = QtCore.pyqtSignal(object)
    def __init__(self):
        QMainWindow.__init__(self)
        uic.loadUi("Test.ui",self)

        self.Iniciar.clicked.connect(lambda:self.ShowImage())

        self.signal.connect(self.Finish)
        self._thread = TheThread(self,self.Fun1)

    def ShowImage(self):
        _movie = QtGui.QMovie("Loader.gif")
        self.Animacion.setMovie(_movie)
        self.Animacion.setScaledContents(True)
        _movie.start()

        self._thread.start()

    def Fun1(self):
        time.sleep(3)
        self.signal.emit(0)

    def Finish(self,signal):
        if signal == 0:
            time.sleep(1)

            self.tabWidget.setCurrentIndex(1)
            # TheThread.kill()
            TheThread.stop()


app = QApplication([])
m = Main()
m.show()
app.exec_()

文件.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QTabWidget" name="tabWidget">
    <property name="geometry">
     <rect>
      <x>30</x>
      <y>0</y>
      <width>691</width>
      <height>441</height>
     </rect>
    </property>
    <widget class="QWidget" name="tab">
     <attribute name="title">
      <string>Tab 1</string>
     </attribute>
     <widget class="QLabel" name="Animacion">
      <property name="geometry">
       <rect>
        <x>220</x>
        <y>40</y>
        <width>231</width>
        <height>171</height>
       </rect>
      </property>
      <property name="text">
       <string>TextLabel</string>
      </property>
     </widget>
     <widget class="QLabel" name="Estatus">
      <property name="geometry">
       <rect>
        <x>220</x>
        <y>270</y>
        <width>271</width>
        <height>16</height>
       </rect>
      </property>
      <property name="text">
       <string>TextLabel</string>
      </property>
     </widget>
    </widget>
    <widget class="QWidget" name="tab_2">
     <attribute name="title">
      <string>Tab 2</string>
     </attribute>
    </widget>
   </widget>
   <widget class="QPushButton" name="Iniciar">
    <property name="geometry">
     <rect>
      <x>30</x>
      <y>460</y>
      <width>75</width>
      <height>23</height>
     </rect>
    </property>
    <property name="text">
     <string>PushButton</string>
    </property>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>21</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

Image.gif

【问题讨论】:

  • 尝试杀死线程对象,而不是它的类:self._thread.stop ()。注意:还有一些地方需要改进

标签: python multithreading pyqt5


【解决方案1】:

您的描述与您的代码不符,也不符合正确的逻辑。

  • 属于 GUI 的 gif 必须在主线程(GUI 线程)中执行,繁重的任务在辅助线程中执行。

  • 不要在主线程中使用time.sleep()。

  • 它执行完繁重的任务并不意味着它执行完gif,你必须停止它。

考虑到上述情况,一种更简单的方法是创建一个位于另一个线程中的 QObject 并在那里执行任务,这允许您添加启动和完成信号。

import os
import time
from functools import partial
from PyQt5 import QtCore, QtGui, QtWidgets, uic


current_dir = os.path.dirname(os.path.realpath(__file__))


def callback():
    # emulate task
    time.sleep(3.0)


class Worker(QtCore.QObject):
    started = QtCore.pyqtSignal()
    finished = QtCore.pyqtSignal()

    @QtCore.pyqtSlot(object)
    def task(self, fun):
        self.started.emit()
        fun()
        self.finished.emit()


class Main(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(Main, self).__init__(parent)

        filename = os.path.join(current_dir, "Test.ui")
        uic.loadUi(filename, self)

        self.Animacion.setScaledContents(True)

        thread = QtCore.QThread(self)
        thread.start()

        self._worker = Worker()
        self._worker.moveToThread(thread)
        self._worker.started.connect(self.show_image)
        self._worker.finished.connect(self.on_finished)

        wrapper = partial(self._worker.task, callback)
        self.Iniciar.clicked.connect(wrapper)

    @QtCore.pyqtSlot()
    def show_image(self):
        _movie = QtGui.QMovie("Loader.gif")
        self.Animacion.setMovie(_movie)

        self.Animacion.movie().start()

    @QtCore.pyqtSlot()
    def on_finished(self):
        self.tabWidget.setCurrentIndex(1)
        self.Animacion.movie().stop()


def main():
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = Main()
    w.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

【讨论】:

    【解决方案2】:

    @eyllanesc 是正确的,您使用了错误的线程,更好的方法是使用 Qt GUI 线程。但是,除了使用stop()kill() 方法(顺便说一句,它们不应该设置为您定义的self._thread 变量而不是TheThread 类?)。这些技术本质上是骇人听闻的,但它们确实有效。

    首先,您可以抛出异常,因为异常总是会阻止代码执行。此示例使用ctypes 库:

    import ctypes
    
    def get_id(self): 
    
        # returns id of the respective thread 
        if hasattr(self, '_thread_id'): 
            return self._thread_id 
        for id, thread in threading._active.items(): 
            if thread is self: 
                return id
    
    def raise_exception(self): 
        thread_id = self.get_id() 
        res = ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 
              ctypes.py_object(SystemExit)) 
        if res > 1: 
            ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0) 
            print('Exception raise failure') 
    
    self._thread.raise_exception();
    

    您还可以使用布尔标志变量,当设置为 true 时,将执行 break 语句,从而结束程序。您可以在 run() 函数中定义线程,然后 join() 方法基本上停止线程:

    def run(): 
       while True:
           global stop_threads 
           if stop_threads: 
              break
    
    self._thread = threading.Thread(target = run)  
    self_thread.start() 
    stop_threads = True
    self_thread.join()
    

    您也可以使用多处理模块,而不是使用线程,您可以使用进程,这非常相似,唯一的区别是线程在相同的内存空间中运行,而进程具有单独的内存。 terminate() 方法杀死进程:

    import multiprocessing 
    import time 
    
    def func(number): 
       for i in range(1, 10): 
           time.sleep(0.01) 
           print('Processing ' + str(number) + ': prints ' + str(number*i)) 
    
    all_processes = [] 
    
    for i in range(0, 3): 
       process = multiprocessing.Process(target=func, args=(i,)) 
       process.start() 
       all_processes.append(process) 
    
    # Kill all processes after 0.03s  
    time.sleep(0.03) 
    for process in all_processes: 
       process.terminate()
    

    最后,您可以通过将线程设置为守护进程来终止线程。默认情况下,线程不会在程序结束时结束,所以守护线程是那些在主程序退出时被杀死的线程:

    def func(): 
       while True: 
          time.sleep(0.5) 
          print('Thread alive, but it will die on program termination') 
    
    self._thread = threading.Thread(target=func) 
    self._thread.daemon = True
    self._thread.start() 
    time.sleep(2)
    sys.exit()
    

    【讨论】:

      猜你喜欢
      • 2016-06-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多