Qt扫盲-Reentrant和线程安全

news/2024/11/1 19:31:08/

Reentrant和线程安全

  • 一、概述
  • 二、Reentrant
  • 三、线程安全
  • 四、Qt类的注意事项

一、概述

在整个文档中, Reentrant 和 thread-safe 术语用于标记类和函数,指明如何在多线程应用程序中使用它们:

  • 一个 thread-safe的函数可以从多个线程同时调用,即使调用使用了共享数据,因为对共享数据的所有引用都是序列化的。
  • 一个 Reentrant 函数也可以从多个线程同时调用Reentrant函数,但前提是每次调用都使用自己的数据。

因此,thread-safe 的函数总是Reentrant的,但 Reentrant 的函数并不总是线程安全的,可以理解Reentrant 是线程不安全的。

扩展一下,如果一个类的成员函数可以在多个线程中安全地调用,只要每个线程使用这个类的不同实例,那么这个类就是Reentrant的。如果类的成员函数可以在多个线程中安全地调用,那么这个类就是线程安全的,即使所有线程都使用这个类的同一个实例。

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

二、Reentrant

c++类通常是Reentrant的,因为它们只访问自己的成员数据。任何线程都可以调用Reentrant类实例的成员函数,只要其他线程不能同时调用同一个类实例的成员函数。例如,下面的Counter类是Reentrant的:

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

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

  1. 将变量的值加载到寄存器中。
  2. 增加或减少寄存器的值。
  3. 将寄存器的值存储到主内存中。

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

三、线程安全

显然,访问必须序列化:线程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类会在构造函数中自动锁定互斥量,并在函数末尾调用析构函数时解锁。锁定互斥量可以确保来自不同线程的访问将被序列化。互斥量数据成员使用可变限定符声明,因为我们需要在value()中锁定和解锁互斥量,这是一个const函数。

四、Qt类的注意事项

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

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

  • 注意:多线程领域的术语并没有完全标准化。POSIX使用的Reentrant和线程安全的定义与其C api有些不同。当在Qt中使用其他面向对象的c++类库时,请确保理解这些定义。

http://www.ppmy.cn/news/20188.html

相关文章

4729. 解密

Powered by:NEFU AB-IN Link 文章目录4729. 解密题意思路代码4729. 解密 题意 给定一个正整数 k, 有 k次询问,每次给定三个正整数 ni,ei,di,求两个正整数 pi,qi,使 nipiqi,eidi(pi−1)(qi−1)1 思路 通过数学推导可以推出 p q …

Git操作

合并远端提交 将远程三次提交合成一次 git -rebase -i HEAD~3:wq 保存 就叫 day 17 再保存 最后 git push -f提交 rebase 与 merge merge 1 新建一个分支 git chekout -b feature-1 添加代码a 并提交 1.1 发现此次提交内容有误 又不想产生两条提交记录 先 git add . gi…

[Vulnhub] DC-4

下载链接&#xff1a;https://download.vulnhub.com/dc/DC-4.zip 同DC-3 这个靶机也是只有一个flag。 全面信息搜集hydra爆破登录和ssh密码teehee命令(写入文件内容)提权/etc/passwd & /etc/sudoers 文件利用 目录 <1> 信息搜集 <2> hydra爆破登录密码 <…

一道编程劝退题,检测你是否适合干编程

前言大家都知道要想成为一名优秀的开发工程师&#xff0c;需要数学基础好&#xff0c;即你要有很强的逻辑思维能力&#xff0c;这里有一道美国斯坦福大学出的一道逻辑思维的测试题&#xff0c;检测你的逻辑思维能力&#xff0c;大家可以看看自己逻辑能力怎么样。题目有一个抽奖…

bug解决:Uncaught (in promise) TypeError: Toast.loading is not a function

在使用vue3做项目时&#xff0c;在其中使用了vant框架&#xff0c;在使用toast的加载组件或者是一些其他组件时&#xff0c;出现了如下的错误&#xff1a;Uncaught (in promise) TypeError: Toast.loading is not a function错误代码如下&#xff1a;Toast.loading({ message: …

C技能树-判断语句

三个数从小到大排序并输出 任意输入3个整数&#xff0c;使用if语句对这3个整数由小到大进行升序排序。请判断下面哪一项无法实现该功能。 #include <stdio.h>/* 交换x和y */ void swap(int* x, int* y) {int temp *x;*x *y;*y temp; }int main(int argc, char** arg…

springboot 入门

springboot是什么 传统的开发模式下&#xff0c;无论是基于xml或注解&#xff0c;都要做许多配置&#xff0c;如果项目中集成越多的其他框架&#xff0c;配置内容也会越多。为了让开发人员以最少的配置去开发应用&#xff0c;springboot诞生了。springboot的原则是约定大于配置…

untiy TextMeshPro(简称TMP)组件详细内容

首先unity官方API对该组件的描述是&#xff1a; TextMeshPro 是 Unity 的最终文本解决方案。它是 Unity UI Text 和旧版 Text Mesh 的完美替代方案。 功能强大且易于使用的 TextMeshPro&#xff08;也称为 TMP&#xff09;使用高级文本渲染技巧以及一组自定义着色器&#xff1b…