Qt学习-可重入和线程安全

 原文地址 Reentrancy and Thread-Safety | Qt 6.5

  • 一个线程安全的函数可以从多个线程被同时调用,即使调用使用共享数据也是如此,因为对共享数据的所有引用都是序列化的。

  • 可重入函数也可以从多个线程被同时调用,但前提是每次调用都使用自己的数据。

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

通过扩展,如果一个类的成员函数可以从多个线程安全地调用,则该类被称为可重入的,只要每个线程使用该类的不同实例。 如果可以从多个线程安全地调用该类的成员函数,则该类是线程安全的,即使所有线程都使用该类的同一个实例

注意:如果 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 同时加载变量的旧值,递增它们的寄存器,然后将其存储回去,它们最终会相互覆盖,并且变量只会递增一次!

线程安全

显然,访问必须串行化:线程 A 必须在线程 B 执行相同步骤之前不间断地(原子地)执行步骤 1、2、3; 或相反亦然。 使类线程安全的一种简单方法是使用 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() 中锁定和解锁 mutex,这是一个 const 函数。

Qt 类注释

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

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值