【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 同时加载变量的旧值,增加其寄存器,然后存储回去,它们最终会互相覆盖,变量仅会增加一次!

线程安全性

显然,访问必须是串行化的:在线程 B 执行相同步骤之前,线程 A 必须无中断地执行步骤 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() 中锁定和解锁互斥体,而 value() 是一个 const 函数。

关于 Qt 类的注释

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

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

注意: 在多线程领域,术语并没有完全统一。POSIX 使用了对其 C API 有些不同的可重入和线程安全的定义。当使用其他面向对象的 C++ 类库与 Qt 一起使用时,请确保理解这些定义。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值