当前位置: 首页 > news >正文

Qt 重入和线程安全

重入和线程安全

在整个文档中,"重入"和 "线程安全 "这两个术语被用来标记类和函数,以表明它们在多线程应用程序中的使用方式:

  • 线程安全函数可以同时被多个线程调用,即使调用使用的是共享数据,因为共享数据的所有引用都已序列化。
  • 重入函数也可以同时被多个线程调用,但前提是每次调用都使用自己的数据。

因此,线程安全的函数总是可重入的,但可重入的函数并不总是线程安全的

推而广之,如果一个类的成员函数可以被多个线程安全调用,只要每个线程使用的是该类的不同实例,那么这个类就是可重入的。如果可以从多个线程安全地调用类的成员函数,即使所有线程都使用类的相同实例,该类也是线程安全的。

注意: 只有当 Qt 类被多个线程使用时,才会被记录为线程安全。如果函数未标记为线程安全或可重入,则不应在不同线程中使用。如果一个类未标记为线程安全或可重入,则该类的特定实例不得从不同线程访问。

重入

C++ 类通常是可重入的,原因很简单,因为它们只访问自己的成员数据。任何线程都可以调用可重入类实例的成员函数,只要其他线程不能同时调用该类同一实例的成员函数。例如,下面的Counter 类就是可重入类:

class Counter
{
public:
    Counter() { n = 0; }

    void increment() { ++n; }
    void decrement() { --n; }
    int value() const { return n; }

private:
    int n;
};

该类不是线程安全的,因为如果多个线程试图修改数据成员n ,结果是未定义的。这是因为++ 和-- 操作符并不总是原子性的。事实上,它们通常扩展为三条机器指令:

  1. 将变量值载入寄存器。
  2. 递增或递减寄存器的值。
  3. 将寄存器的值存储回主内存。

如果线程 A 和线程 B 同时加载变量的旧值、递增寄存器并将其存储回去,那么它们最终会互相覆盖,而变量只会递增一次!

导致类不可重入的典型设计模式

  • 使用静态数据成员或全局状态
    • 如果类依赖静态变量或全局资源,多个实例或线程共享这些状态时可能引发冲突。
  • 单例模式未正确实现线程安全
    • 单例类若在初始化时未加锁,多线程可能创建多个实例,破坏单例语义。
  • 未保护共享外部资源
    • 类若操作文件、数据库连接等外部资源时未加锁,多线程访问会导致资源冲突。
  • 依赖非线程安全的第三方库
    • 若类封装了非线程安全的第三方 API,直接暴露给多线程环境会导致不可重入。
  • 成员函数修改共享内部状态
    • 若类的成员函数修改共享的成员变量,且未同步,多线程调用会破坏状态。
    • 比如缓存类(不可冲入):多线程调用 add() 可能导致 std::map 内部状态损坏。
    • class Cache {
      private:
          std::map<std::string, std::string> data_;
      public:
          void add(const std::string& key, const std::string& value) {
              data_[key] = value;  // 多线程写入可能破坏 map 结构
          }
      };

线程安全

显然,访问必须序列化:线程 A 必须不间断(原子地)执行步骤 1、2、3,然后线程 B 才能执行相同的步骤;反之亦然。让类具有线程安全的简单方法是使用QMutex 保护对数据成员的所有访问:

class Counter
{
public:
    Counter() { n = 0; }

    void increment() { QMutexLocker locker(&mutex); ++n; }
    void decrement() { QMutexLocker locker(&mutex); --n; }
    int value() const { QMutexLocker locker(&mutex); return n; }

private:
    mutable QMutex mutex;
    int n;
};

QMutexLocker 类会在构造函数中自动锁定互斥体,并在函数结束调用析构函数时解除锁定。锁定互斥确保来自不同线程的访问将被序列化。mutex 数据成员使用mutable 限定符声明,因为我们需要在value() 中锁定和解锁互斥体,而 是一个常量函数。

Qt 类注意事项

许多 Qt 类都是可重入的,但它们并不是线程安全的,因为如果让它们成为线程安全的,就会产生重复锁定和解锁QMutex 的额外开销。例如,QString 是可重入的,但不是线程安全的。您可以安全地同时从多个线程访问QString 的不同实例,但无法安全地同时从多个线程访问QString 的同一实例(除非您使用QMutex 保护自己的访问)。

有些 Qt 类和函数是线程安全的。这些主要是与线程相关的类(如QMutex )和基本函数(如QCoreApplication::postEvent() )。

注: 多线程领域的术语并不完全标准化。POSIX 使用的可重入和线程安全定义与其 C API 有些不同。在 Qt 中使用其他面向对象的 C++ 类库时,请务必理解这些定义。

Reentrancy and Thread-Safety | Qt 6.8

相关文章:

  • 【线程安全问题的原因和方法】【java形式】【图片详解】
  • 深入理解 tree 命令行工具:目录结构可视化的利器
  • LeetCode hot 100 每日一题(15)——48.旋转图像
  • python --face_recognition(人脸识别,检测,特征提取,绘制鼻子,眼睛,嘴巴,眉毛)/活体检测
  • vue数据重置
  • RFID测温技术:提升电缆安全监测的理想选择
  • docker pull时报错:https://registry-1.docker.io/v2/
  • 开源链动2+1模式与AI智能名片赋能的S2B2C共享经济新生态
  • 批量配置Linux ~/.bash_profile
  • 医学图像分割数据集肺分割数据labelme格式6299张2类别
  • 数据库基础知识点(系列二)
  • Atlas 800I A2 双机直连部署DeepSeek-R1-w8a8
  • SAP Activate Methodology in a Nutshell Phases of SAP Activate Methodology
  • 位运算题目:最大单词长度乘积
  • Netty源码—客户端接入流程
  • Linux应用:select、poll
  • 算法每日一练 (18)
  • 23种设计模式-创建型模式-原型
  • 4、操作系统结构和发展史
  • 深入理解8086指令集与反汇编技术
  • 人社部:将制定提前领取个人养老金相关办法
  • 美国通过《删除法案》:打击未经同意发布他人私密图像,包括“深度伪造”
  • 外交部亚洲司司长刘劲松向菲方严肃交涉
  • 韩国下届大选执政党初选4进2结果揭晓,金文洙、韩东勋胜出
  • 直播电商行业代表呼吁:携手并肩伸出援手助力外贸企业攻坚克难
  • 人到中年为何腰围变粗?科学家发现腹部脂肪增加的细胞元凶