pyqt5 的多线程(QThread)遇到的坑(一)

前言

不管网上搜索QThread用法还是网上授课老师的教程,对QThread的介绍都是最简单最基本的功能,如下:
创建线程类:

class Thread(QThread):
 	sinOut = pyqtSignal(str)   # 定义信号便于传递数据
    def __init __(self):
        super(Thread,self).__ init __()
    def run(self):
        #线程相关的代码
        ...
        # 发射信号
        self.sinOut.emit(file_str)

创建一个新的线程

thread = Thread()
thread.start()

举例的代码也是上面这几行代码的反复使用,比如弄个定时器显示个时间什么的。但是,再我们平时开发中,并不会这么简单,那么坑就来了。

第一个坑:给线程类传参

在UI中调用线程一般是因为一些比较耗时的工作会导致界面假死,但是往往这些耗时工作需要一些现有数据,那么就需要给线程传参,上面线程内定义的信号传递数据是把线程内的数据传递出去,但是我这里是想说把数据传递进来,让线程进行一些耗时运算,运算完了再把运算结果数据传递出去。这里只要注意,传参只能在线程类的 init(self,agrs)中传参,run(self)是不能传参的!传参的类型很多,可以是数据也可以是对象。

第二个坑:传递一个类的实例

比如我定义了一个类进行数据运算,运算耗时很长,我需要把数据运算类的工作使用多线程处理, 数据运算类:

class ModifyData(object):
    def __init__(self, data):
        self.data = data  # 接收原始数据
    def analyze(self):
    	...  # 数据处理过程省略
        return self.data   # 返回数据处理结果

那么在UI窗口中就要使用多线程了,好吧,先定义一个线程类,上面第一个坑提到了传参的问题,我们直接在线程类初始化时加入参数 argument_,这个参数是用来接受上面定义的数据处理类ModifyData():

class MoreThreadUse(QtCore.QThread):
    update_date = QtCore.pyqtSignal(pandas.DataFrame)   # 定义信号
    def __init__(self, argument_):
        super().__init__()
        self.fuc = argument_
    def run(self):
        a = self.fuc.analyze()
        self.update_date.emit(a)
        time.sleep(10)
        print("")

这里还需要一个UI的工作界面类,我们是在这个UI中使用线程进行数据分析的,但是在这个Ui类中,我们如何把前面的数据分析类交给多线程来处理呢?直接把类名ModifyData传递过去吗?对不起!当你用start()启动线程的时候会直接崩溃!有两种传递方式:第一、先把数据处理类ModifyData实例化,再把实例化对象传参过去!第二, 把原始数据传参给自定义多线程类,在多线程类的初始化中方法中实例化数据处理类ModifyData;下面是UI的工作界面类,使用第一种方法开启多线程:

class ThreadUI(QtWidgets.QDialog):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("UI")
        self.resize(500, 300)
        self.input_line = QtWidgets.QLineEdit(self)
        self.input_line.resize(400, 200)
    def abc(self):
        path = "E:\\*****\\***.xls"
        data = pandas.read_excel(path)
        ann = ModifyData(data)
        self.thread_1 = MoreThreadUse(ann)
        self.thread_1.update_date.connect(self.handle_display)
        self.thread_1.start()  
    def handle_display(self, data):
        self.input_line.setText(data)
第三个坑:信号传参

大家都在说,在线程类种定义一个信号,方便与传递数据,虽然可以传递很多常见数据,但是并非所有数据都可以传递,上面的ui类种可以看到,要传递的数据是pandas打开的一个excel表格,其实这个表格有几万条数据,上面的往线程内传参解决了,那使用线程数据处理完了,如何把数据传递出来呢?用信号吗?不可以,直接崩溃了!因为数据格式是pandas.DataFrame ,信号不能传递这类数据,肯定还有其他类型不能传递,这里不列举了,只要知道这回事就可以了,那么怎么处理呢?信号是肯定不能用了,我暂时没想到更好的办法,我这里是使用了写入文件处理的,因为我的数据分析非常耗时,所有分析过程种可以做很多其他事情,直接把分析结果写入一个文件就ok了。正确的代码逻辑如下:

import sys
from PyQt5 import QtCore, QtGui, QtWidgets
import pandas
import time
class MoreThreadUse(QtCore.QThread):
    # 多线程,传递参数为一个类的实例,不用信号传递数据,直接把数据写入文件
    def __init__(self, argument_):
        super().__init__()
        self.fuc = argument_
    def run(self):
        self.fuc = ModifyData(self.fuc)
        data = self.fuc.analyze()
        data.to_excel("D:\\****.xls")  # 直接把数据处理结果写入文件,使用pandas.to_xxx写文件很方便
        time.sleep(10)
        print(a)
class ThreadUI(QtWidgets.QDialog):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("UI")
        self.resize(500, 300)
        self.input_line = QtWidgets.QLineEdit(self)
        self.input_line.resize(400, 200)
    def abc(self):
        path = "E:\\*****\\***.xls"
        data = pandas.read_excel(path)
        ann = ModifyData(data)
        self.thread_1 = MoreThreadUse(data)
        self.thread_1.start()  
    def handle_display(self, data):
        self.input_line.setText(data)
class ModifyData(object):
    def __init__(self, data):
        self.data = data
    def analyze(self):
		...  # 数据处理过程省略
    	return self.data   # 返回数据处理结果
if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    main = ThreadUI()
    main.abc()
    main.show()
    sys.exit(app.exec_())
最后一个坑:创建线程类的变量

上面的例子种可以看到,线程类实例化时变量是 self.thread_1 一定要加上self,如果不加,thread_1就是一个局部变量,当其所在方法运行结束的时候,它的生命周期也都结束了,但是这个线程里的程序很有可能还没有运行完!可能会报错:QThread :Destroyed while thread is still running!

  • 56
    点赞
  • 177
    收藏
    觉得还不错? 一键收藏
  • 21
    评论
### 回答1: PyQt5中的QThread类可以用于创建多线程应用程序。在多线程应用程序中,同步是非常重要的,因为多个线程可能会同时访问同一个资源。为了确保线程安全,可以使用互斥锁或信号量等同步机制来保护共享资源。在PyQt5中,可以使用QMutex和QSemaphore类来实现同步。QMutex类提供了一个互斥锁,可以确保同一时间只有一个线程可以访问共享资源。QSemaphore类提供了一个信号量,可以控制同时访问共享资源的线程数量。在使用QThread创建多线程应用程序时,需要注意同步问题,以确保应用程序的正确性和稳定性。 ### 回答2: PyQt5多线程方面提供了很好的支持,通过QThread类,我们可以创建多个线程,从而实现并发操作。QThread是Qt的线程类,它封装了线程的所有操作,如启动、停止、暂停、恢复等。下面我们来探讨一下如何在PyQt5中实现多线程同步。 1.线程间通信 由于多个线程是并发执行的,如果在某个线程中修改了共享变量,可能会对其他线程产生影响,因此为了保证程序的正确性,在多线程编程中必须保证线程间同步,确保线程之间通信的正确性、完整性和准确性。为了实现线程间通信,在PyQt5中提供了多种方法: a.使用信号和槽方法:信号和槽方法是Qt中的一种消息传递机制,由于多个线程之间不能访问同一个变量,只能通过信号和槽方法来交换信息。 b.使用共享变量:通过将变量声明为共享变量,多个线程可以访问这个变量,通过加锁和解锁保证同步。 c.使用队列:在一个线程中,将共享变量或计算结果放入队列中,然后在另一个线程中取出这些结果。 2.加锁和解锁 为了保证多线程的同步,PyQt5提供了多种锁的类型,如互斥锁、读写锁、信号量、条件变量等。互斥锁是最简单的一种锁,通常用于多个线程之间对同一共享资源的互斥访问。在一个线程中执行临界区代码时,需要加锁,以避免其他线程同时访问这个共享资源。在PyQt5中,可以使用QMutex类或QReadWriteLock类实现互斥锁。 3.线程池 线程池是一种常见的多线程应用模型,它由一个线程池管理器、工作线程和任务队列组成。线程池管理器负责创建、销毁和管理工作线程,工作线程执行实际的任务,任务队列用于存储等待执行的任务。线程池的优点是可以在一定程度上控制线程的数量,防止因线程过多导致程序崩溃,提高了程序的性能。 总之,在PyQt5中实现多线程同步需要注意线程间通信、加锁和解锁以及使用线程池等方法来提高程序的效率和可靠性。除此之外,还需要注意程序的实际应用场景和数据结构的合理设计,以达到更好的多线程同步效果。 ### 回答3: 在PyQt5中,QThread用于在单独的线程中执行耗时或长时间运行的任务。它是基于QObject的子类,可以通过信号和槽与其他线程和主线程进行通信。但是,在使用QThread时,需要注意多线程同步的问题,以避免出现不稳定和不可预测的行为。 为了避免多线程同步问题,可以采用以下几种方法: 1. 使用锁(QMutex)或信号量(QSemaphore): 使用锁或信号量可以确保多个线程不会同时访问共享资源。在一个线程中,可以使用QMutex.lock()函数来获得锁,使用QMutex.unlock()函数来释放锁。当一个线程正在使用共享资源时,其他线程将被阻塞,直到锁被释放为止。同样地,可以使用QSemaphore.acquire()和QSemaphore.release()函数实现信号量。 2. 使用互斥量(QReadWriteLock): 互斥量是一种特殊的锁,用于控制多个线程对同一资源的访问。QReadWriteLock类提供了读写锁,其中读锁可被多个线程同时保持,但写锁只能由一个线程保持,并且在此期间其他线程将被阻塞。 3. 使用信号和槽: 在使用QThread时,可以通过信号和槽来实现多线程同步。可以在主线程中定义一个信号,并将其连接到QThread中的槽。当QThread中的任务完成时,可以通过信号来通知主线程更新UI。 总之,多线程同步是编写可靠和高质量的多线程代码的关键部分。需要根据应用程序的要求选择合适的同步机制,并在代码中正确地实现它们。通过正确使用多线程同步机制,可以确保应用程序的可靠性和性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值