QT的重入和线程安全[翻译+思考]

翻译自https://doc.qt.io/qt-5/threads-reentrancy.html.


重入和线程安全

在整个文档中,术语可重入和线程安全用于标记类和函数-----以指示如何在多线程应用程序中使用它们:
线程安全函数,可以同时被多个线程调用,即使调用使用了共享数据,因为对共享数据的使用已序列化。
可重入函数也可以被多个线程同时调用,条件是只有当每个调用使用自己的数据。

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

如果一个类的成员函数可以被多个线程安全地调用,只要每个线程使用该类的不同实例,则称该类是可重入的。如果一个类可以被多个线程安全地调用其成员函数,则该类是线程安全的,条件是所有线程都使用该类的相同实例。
注意: Qt 类仅在打算由多个线程使用时才记录为线程安全的。[翻译注: 肯定啊, 为了效率啊]
           如果一个函数未标记为线程安全或可重入,则不应在不同线程中使用它。
           如果一个类未标记为线程安全或可重入,则不应从不同线程访问该类的特定实例。
重入性
C++ 类通常是可重入的,因为它们通常只访问自己的成员数据。任何线程都可以在可重入类的实例上调用成员函数,条件是没有其他线程会同时调用该类的同一实例上的成员函数。例如,Counter下面的类是可重入的:

class Counter
{
public:
    Counter() { n = 0; }
 
    void increment() { ++n; }
    void decrement() { --n; }
    int value() const { return n; }
 
private:
    int n;
};

该类不是线程安全的,因为如果多个线程尝试修改数据成员n,结果是未定义的。这是因为++and--运算符并不总是原子的。事实上,它们通常扩展为三个机器指令:
    1.将变量的值加载到寄存器中。
    2.增加或减少寄存器的值。
    3.将寄存器的值存储回主存储器。

线程安全

显然,访问必须是序列化的:线程 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;
};

Qt 类的注意事项

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

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

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

QT帮助说明:
QT会在类中描述其是否可重入,如图:

问题来了:
多线程只读访问QString或者QT的容器,是否可行.
我个人谨慎表示可行, 且我没法论证,以下图片是参考

 

posted @ 2022-09-06 22:08  xingzaicpp  阅读(147)  评论(0编辑  收藏  举报