PySide6/PyQT多线程之 线程锁/线程安全

news2024/12/24 2:07:36

在这里插入图片描述

前言

PySide6/PyQT多线程同时访问同一个共享资源或对象,程序可能会出现预期之外的结果。所以需要考虑线程安全问题。

使用PySide6/PyQT开发GUI应用程序,在多个线程同时访问同一个共享对象时候,如果没有进行同步处理那就可能会导致数据不一致或者一些意料之外的问题发生。

所以使用多线程就得考虑线程安全,而线程安全最简单的处理方法就是 线程锁 。当然,使用信号槽机制(Siganl、Slot)也可以完美解决这个问题。

本篇文章使用 QMutex 来解决多线程占用共享资源的问题。
至于 信号槽机制(Siganl、Slot)在另外一篇文章有介绍,所以这里不做过多赘述。

知识点📖📖

本文用到的几个PySide6的知识点及链接。

作用链接
创建新线程QThread
线程同步机制,用于协调多个线程之间对共享资源的访问QMutex
对象间通信的机制,允许对象发送和接收信号Signal
用于响应Signal信号的方法Slot

实现

注意事项

当然,如果可以解决线程安全的问题,那就当这里没说。

关于保证线程安全,可以遵循以下几个原则:

  • 尽量不要在多个线程中访问和修改同一个对象;
  • 如果必须要访问和修改同一个对象,需要使用线程同步机制,例如信号槽、互斥锁、条件变量等;
  • 避免使用共享状态,例如全局变量,尽量将状态封装在对象内部,并使用线程安全的方式访问和修改状态;
  • 不要使用原生的线程库,选择使用 Qt 提供的 QThreadQThreadPool 等线程库。

上面这几点其实差不多一个意思,有些概念就行。


示例代码

线程不安全代码

代码释义

Worker类

  • 继承了QThread类,并创建 信号valueChanged
  • 接收两个参数,一个为name,一个为MainWindow类实例对象本身;
  • 循环5次,每次为 MainWindow实例count累加1;
  • 并使用valueChanged 信号将执行结果和执行次数发送出去;

MainWindow类

  • 继承了QWidget,实现了包含一个按钮和一个标签的窗口;
  • setup_thread函数为窗口布局;
  • setup_thread函数 实例化两个Worker类,并将它们的信号连接到Slot槽函数 thread_finished
  • 按钮绑定了 start_threads函数
  • thread_finished函数Slot槽函数,用于接收 Worker 的信号发送的结果。

代码中两个线程同时访问了 self.count,结合本篇文章标题,如果程序没出错,那在这里一定会出错。看看下面的运行结果。


# -*- coding: utf-8 -*-


import sys
import time

from PySide6.QtCore import (QThread, Signal, Slot)
from PySide6.QtWidgets import (QApplication, QLabel, QPushButton, QVBoxLayout, QWidget)


class Worker(QThread):
    valueChanged = Signal(tuple)

    def __init__(self, name, main_window):
        super().__init__()
        self.name = name
        self.main_window = main_window

    def run(self):
        for i in range(5):
            self.main_window.count += 1
            time.sleep(0.1)
            self.valueChanged.emit((self.name, self.main_window.count))


class MainWindow(QWidget):
    def __init__(self):
        self.count = int()

        super().__init__()
        self.setup_ui()
        self.setup_thread()

    def setup_ui(self):
        layout = QVBoxLayout()
        self.label = QLabel("Count: 0", self)

        self.btn_start = QPushButton("Start", self)
        self.btn_start.clicked.connect(self.start_threads)

        layout.addWidget(self.label)
        layout.addWidget(self.btn_start)
        self.setLayout(layout)
        self.setGeometry(300, 300, 250, 150)
        self.show()

    def setup_thread(self):
        self.worker1 = Worker('thread_1', self)
        self.worker2 = Worker('thread_2', self)
        self.worker1.valueChanged.connect(self.thread_finished)
        self.worker2.valueChanged.connect(self.thread_finished)

    def start_threads(self):
        self.worker1.start()
        self.worker2.start()

    @Slot(tuple)
    def thread_finished(self, value):
        print(str(value))
        self.label.setText(f"{str(value)}")


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = MainWindow()
    sys.exit(app.exec())

运行结果

代码运行效果如下图所示:

  • 如果程序没有出错,那应该是按照顺序打印 1~10
  • 但这个并不是按照顺序的,那就是说明它们在争夺 self.count时候出现了岔子;
  • 互相争夺共享资源,出现了问题。看后面来修正这个问题。

在这里插入图片描述

QMutex 线程安全

线程同步机制,用于协调多个线程之间对共享资源的访问。

PySide6中可以通过QMutex实现线程锁。QMutex是一个用于同步线程执行的互斥锁,可以保护共享资源不受多个线程同时访问以及修改。

代码释义

Worker类

  • 这一份代码上面的基本一致,只是加了一把锁;
  • 在会争夺共享资源的代码前 上锁, 在会争夺共享资源的代码后 释放锁;

Worker类

  • 这一份代码上面的基本一致,只是加了一把锁;
# -*- coding: utf-8 -*-


class Worker(QThread):
    valueChanged = Signal(tuple)

    def __init__(self, name, mutex, main_window):
        super().__init__()
        self.name = name
        self.mutex = mutex
        self.main_window = main_window

    def run(self):
        for i in range(5):
            # 访问共享资源的代码前 上锁
            self.mutex.lock()
            self.main_window.count += 1
            time.sleep(0.1)
            self.valueChanged.emit((self.name, self.main_window.count))
            # 访问共享资源的代码后 释放锁
            self.mutex.unlock()


class MainWindow(QWidget):
    def __init__(self):
        self.count = int()
        self.mutex = QMutex()  # 定义锁对象
        super().__init__()
        self.setup_ui()
        self.setup_thread()

    def setup_ui(self):
        ....

    def setup_thread(self):
        self.worker1 = Worker('thread_1', self.mutex, self)
        self.worker2 = Worker('thread_2', self.mutex, self)
        self.worker1.valueChanged.connect(self.thread_finished)
        self.worker2.valueChanged.connect(self.thread_finished)

运行结果

现在再来看到运行结果,就是想要的结果了~

在这里插入图片描述

更优雅的QMutex 线程安全

查阅官网得知,在使用QMutex 锁时候,配合 QMutexLocker 使用,可以很容易地确保锁定和解锁的执行是一致的。

QMutexLocker 它的作用是在实例化时自动加锁,离开作用域时自动解锁,从而保证临界区代码的排他性,避免了资源竞争和死锁的发生。

只需要在上面 Worker类run中修改如下:

from PySide6.QtCore QMutexLocker


def run(self):
    for i in range(5):
    	# 自动上锁、释放锁
        with QMutexLocker(self.mutex):
            self.main_window.count += 1
            time.sleep(0.1)
            self.valueChanged.emit((self.name, self.main_window.count))

后话

本次分享到此结束,
see you~🐱‍🏍🐱‍🏍

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/446845.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HTML5 <q> 标签、HTML5 <rp> 标签

HTML5 <q> 标签 实例 HTML5 <q>标签用于定义一个短引用。请参考一下内容&#xff1a; 标记一个短的引用&#xff1a; <p>WWFs goal is to: <q>Build a future where people live in harmony with nature.</q> We hope they succeed.</p&g…

019:Mapbox GL加载天地图(影像瓦片图)

第019个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中加载天地图(影像瓦片图)。 直接复制下面的 vue+mapbox源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代码(共80行)相关API参考:专栏目标示例效果 配置方式 1)查看基础设置:h…

2023年定向增发研究报告

第一章 行业概况 定向增发是增发的一种&#xff0c;是指上市公司向符合条件的少数特定投资者非公开发行股份的行为&#xff0c;有时也称“定向募集”或“私募”。定向增发的发行价格由参与增发的投资者竞价决定&#xff0c;发行程序与公开增发相比较为灵活。一般认为&#xff…

中国地图标准坐标和投影参数

目录 一、地理坐标 二、投影坐标 三、ArcGIS投影变换 四、说明 一、地理坐标 GCS_Krasovsky_1940&#xff08;克拉索夫斯基_1940椭球体&#xff09; 具体参数如下图&#xff1a; 每个国家或地区都有各自的基准面&#xff0c;我们通常所说的北京54坐标系、西安80坐标系实际上…

天梯赛练习题集

L2-005 集合相似度 find函数&#xff0c;Nt用集合关系求 #include <bits/stdc.h> #define ios ios::sync_with_stdio(0),cin.tie(0) #define PII pair<int,int> typedef long long ll; const int N1e610; const int inf0x3f3f3f3f;using namespace std; int n,k;…

深度学习(9)之 easyOCR使用详解

easyOCR使用详解 本文在 OCR-easyocr初识 基础上进行修改EasyOCR 是一个python版的文字识别工具。目前支持80中语言的识别。其对应的 github 地址&#xff1a;EasyOCR可以在网站版测试 demo 测试效果&#xff1a;https://www.jaided.ai/easyocr/其在字符识别上的效果如下&…

学系统集成项目管理工程师(中项)系列07_信息(文档)管理

1. 信息系统相关信息&#xff08;文档&#xff09; 1.1. 是指某种数据媒体和其中所记录的数据 1.2. 永久性 1.3. 由人或机器阅读 1.4. 仅用于描述人工可读的东西 2. 分类 2.1. 开发文档 2.1.1. 可行性研究报告和项目任务书 2.1.2. 需求规格说明 2.1.3. 功能规格说明 …

MapReduce高级-读写数据库

MapReduce 读取数据库 为什么要读写数据库 本质上讲数据库是存储数据的介质&#xff0c;MapReduce是处理数据的计算引擎。通常企业会使用关系型数据库&#xff08;RDBMS&#xff09;来存储业务的相关数据&#xff0c;随着业务数据的规模越来越大&#xff0c;不可避免的存在性…

C++ -3- 类和对象 (中) | 拷贝构造函数 赋值运算符重载(二)

文章目录 4.拷贝构造函数什么是拷贝构造函数&#xff1f;应用——示例&#xff1a;日期计算器什么情况下需要自己实现拷贝构造函数&#xff1f; 5.赋值运算符重载运算符重载&#xff08;重要&#xff09;赋值运算符重载 拷贝构造函数和赋值重载函数 4.拷贝构造函数 什么是拷贝…

【进阶C语言】静态版通讯录的实现(详细讲解+全部源码)

前言 &#x1f4d5;作者简介&#xff1a;热爱跑步的恒川&#xff0c;正在学习C/C、Java、Python等。 &#x1f4d7;本文收录于C语言进阶系列&#xff0c;本专栏主要内容为数据的存储、指针的进阶、字符串和内存函数的介绍、自定义类型结构、动态内存管理、文件操作等&#xff0…

Javaee Spring JdbcTemplate基本使用查询数据库表的内容 基于xml配置方式

目录 哈哈哈哈&#xff0c;说好是要写一篇关于jdbcTemplate的基本使用&#xff0c;貌似说跑题了&#xff0c;但是主体还是用jdbctemplate实现的&#xff0c;有耐心看完的话相信能有点点收获的哦&#xff01; 项目结构&#xff1a; 用到的数据库: 小结&#xff1a; 遇到了个小…

【Python_Opencv图像处理框架】图像基本操作

写在前面 很幸运能选择Python语言进行学习&#xff0c;这是有关Opencv的图像处理的第一篇文章&#xff0c;讲解了有关图像处理的一些基础操作&#xff0c;作为初学者&#xff0c;我尽己所能&#xff0c;但仍会存在疏漏的地方&#xff0c;希望各位看官不吝指正❤️ 写在中间 1…

Docker容器---介绍、安装

Docker基本管理 一、Docker概述1、IT架构2、什么是docker3、Docker特点4、Docker与KVM区别 二、Docker核心概念1、镜像 容器 仓库2、虚拟架构有哪些 三、Docker使用场景1、Docker在内核中支持的2种重要技术2、应用场景 四、Docker安装1、YUM安装docker2、设置阿里云镜像源3、查…

Java多线程初阶(一)(图片+源码+超详细)

线程的概念参照以往的这篇文章&#x1f43b; 目录 1.创建线程 1.1 继承Thread类 1.2 实现Runnable接口 eg&#xff1a;常用的简写方式 2.Thread类中的常用API 3. start方法和run方法 4. 继承Thread类启动新线程的逻辑 5. 实现Runnable接口启动新线程的逻辑 6. 线程相关…

Linux基础—日志分析

Linux基础—日志分析 一、日志的功能1.日志消息的级别2.设备字段说明 二、日志文件的分类1.内核及系统日志2.用户日志3.程序日志 三、日志文件1.日志文件查看2.主要日志文件介绍3.日志管理策略 一、日志的功能 用于记录系统、程序运行中发生的各种事件 通过阅读日志&#xff0c…

图像描述算法排位赛:SceneXplain与MiniGPT-4谁将夺得桂冠?

如果你对 AI 前沿感兴趣&#xff0c;本场「图像描述算法排位赛」绝对是你不能错过的&#xff01;在这场较量中&#xff0c;SceneXplain 和 MiniGPT-4 将会比试&#xff0c;谁将摘得这场比赛的桂冠&#xff1f; &#x1f4ce; 直接上手体验&#xff1a;scenex.jina.ai 背景介绍 …

手机存储数据恢复软件哪个好用?试了10款,我只认准这一款!

案例&#xff1a;手机存储数据恢复软件哪个好用&#xff1f; 【有没有好介绍的苹果数据恢复软件&#xff1f;可以恢复好几年数据的那种软件&#xff1f;求推荐&#xff01;】 手机中的数据是用户平时使用手机时不可避免的内容&#xff0c;这些数据包括照片、视频、音乐、文件等…

Baumer工业相机堡盟工业相机如何通过BGAPISDK的软触发实现两相机同步采集(C++)

Baumer工业相机堡盟工业相机如何通过BGAPISDK的软触发实现两相机的同步采集&#xff08;C&#xff09; Baumer工业相机Baumer工业相机的高速同步采集的技术背景Baumer工业相机通过BGAPI SDK在回调函数里同步保存图像工业相机在回调函数BufferEvent保存工业相机使用软触发进行同…

【观察】华为:新一代楼宇网络,使能绿建智慧化

“碳达峰”、“碳中和”目标是我国生态文明建设和高质量可持续发展的重要战略安排&#xff0c;将推动全社会加速向绿色低碳转型。作为全球既有建筑和每年新建建筑量最大的国家&#xff0c;大力发展绿色建筑对中国全方位迈向低碳社会、实现高质量发展具有重要意义。 《“十四五”…

使用FirmAE 对zyxel路由器固件仿真实践 | 信息安全

一、FirmAE简介 FirmAE 是一个执行仿真和漏洞分析的全自动框架。FirmAE 使用五种仲裁技术显著提高仿真成功率&#xff08;从Firmadyne的 16.28% 提高到 79.36%&#xff09;。 FirmAE的整体架构为如上图所示。与Firmadyne类似&#xff0c;FirmAE在预先构建的自定义Linux内核和库…