所谓原子操作,即一系列复杂的操作能一气呵成,中间不被其他的操作打断。这在多线程程序中尤其常见,但要实现这种功能,既要考虑程序的良好设计,又要关心特定平台的体系结构和相关编译器对原子特性的支持程度。所以,为了简化这个过程,Qt为我们提供了QAtomicInteger模板类,该类封装了大量与原子操作相关的细节和底层特性,为我们提供了方便易用的上层接口。虽然,该类并不能解决所有的原子操作问题,比如在不同的内存模型下,怎么保证对共享变量的原子访问,还需我们人为的控制,但它已经大大减轻了我们的工作复杂度。

QAtomicInteger模板类主要为我们提供了整数常用的原子操作,如 reference counting、test-and-set、fetch-and-store、fetch-and-add。QAtomicInteger,顾名思义,该类只能应用于整数类型,那么我们下来看下在Qt中哪些整型能使用该类进行原子操作。如下表:

位数 类型
8-bit char, signed char, unsigned char, qint8, quint8
16-bit short, unsigned short, qint16, quint16, char16_t(c++11)
32-bit int, unsigned int, qint32, quint32, char32_t(c++11)
64-bit long long, unsigned long long, qint64, quint64
platform-specific size long, unsigned long
pointer size qintptr, quintptr, qptrdiff

在上表中,只有32-bit 和 pointer-sized 的实例在所有的平台上都能得到保证。但对其他大小的支持需要看特定的编译器和运行改程序的处理器。为了测试自己的平台是否支持某种类型,可以使用Qt提供的宏Q_ATOMIC_INT{nn}_IS_SUPPORTED,这里的nn就是你想测试的类型的位数。
       下面,在具体看QAtomicInteger提供的操作之前,我们先来看一下与原子操作有关的内存顺序(内存模型)。

刚才我们说到QAtomicInteger为我们提供了几种原子操作 test-and-set、fetch-and-store、fetch-and-add。其实这些函数的实现都定义了一种内存顺序的语义,这个语义描述了当处理器执行原子语句时怎么访问这些原子语句及其 前后的内存。因为当代的处理器架构允许对内存进行随意的访问,所以,为了让程序在所以的处理器上都能正确执行,使用一种合适的内存访问语义是至关重要的。在Qt中,为我们提供了4中内存模型:

Relaxed - 即不具体指定内存访问的顺序,编译器和处理器可以自由的对内存访问进行重新排序。
Acquire  - 原子操作之后的内存访问(已程序的顺序)不会在原子操作之前被重新排序。
Release - 原子操作之前的内存访问(已程序的顺序)不会在原子操作之后被重新排序。
Ordered - Acquire 和 Release 的组合。
       接下来,我们具体看下相关的原子操作API:
       Reference counting
       函数ref() 和 deref() 提供了高效的引用计数API。这些函数的返回值表明了什么时候最后一个引用被是释放了。这些功能可以用来实现我们自己的隐式共享类。如下代码所示:
MySharedType &MySharedType::operator=(const MySharedType &other)
{
(void) other.data->atomicInt.ref();
if (!data->atomicInt.deref()) {
// The last reference has been released
delete d;
}
d = other.d;
return *this;
}
       
        Test-and-set
        这些函数完成的功能是如果QAtomicInteger的当前值等于我们传入的期望值,则test-and-set函数会为其赋一个新值,然后返回true。如果当前值不等于传入的期望值,则这些函数声明也不干,直接返回false。即等价于一下的代码逻辑:
if (currentValue == expectedValue) {
currentValue = newValue;
return true;
}
return false;

在QAtomicInteger中为我们提供了4个test-and-set函数,分别是:testAndSetRelaxed()、testAndSetAcquire()、testAndSetRelease()、testAndSetOrdered()。其实就是以不同的内存模型进行操作。

Fetch-and-store
         fetch-and-store 函数的功能是读取QAtomicInteger对象的当前值,并且为它设置一个我们传入的新值,然后返回读取到的旧值。该操作等同与以下的代码逻辑:
int originalValue = currentValue;
currentValue = newValue;
return originalValue;

在QAtomicInteger中有4个fetch-and-store函数:fetchAndStoreRelaxed()、fetchAndStoreAcquire()、fetchAndStoreRelease()、fetchAndStoreOrdered()。
         
         Fetch-and-add
         fetch-and-add函数读取QAtomicInteger对象的当前值,然后为它加上我们传入的值,最后返回原来的值。其执行逻辑类似于下面的代码:
int originalValue = currentValue;
currentValue += valueToAdd;
return originalValue;

在QAtomicInteger中有4个fetch-and-add方法: fetchAndAddRelaxed()、fetchAndAddAcquire()、fetchAndAddRelease()、fetchAndAddOrdered()。

特性测试相关的API
          提供一个平台无关的、能应用于所以处理器的原子操作API是有挑战性的。所以,QAtomicInteger类提供的API能保证在所有的处理器的完成原子操作,但是,并不是所以的处理器都支持QAtomicInteger所提供的这些操作。所以,在使用这些操作之前,检测一下当前处理器是否支持某个API是很重要的。
          所以Qt提供了大量的宏,你可以使用这些宏在编译器就可以检测你的硬件是否支持某个特性。这些宏会告诉你你的硬件是 支持该操作、有时支持该操作、不支持该操作。并且,这些宏有大致相同的形式,方便记忆,类似于 Q_ATOMIC_INTnn_OPERATION_IS_HOW_NATIVE。其中,nn是你要测试的整形的位数,operation是REFERENCE_COUNTING、TEST_AND_SET、FETCH_AND_STORE、FETCH_AND_ADD 其中之一,how是ALWAYS、SOMETIMES、NOT其中之一。并且,对应每种组合只有一个确定的宏。例如,如果Q_ATOMIC_INT32_REFERENCE_COUNTING_IS_ALWAYS_NATIVE 被定义了,那么Q_ATOMIC_INT_REFERENCE_COUNTING_IS_SOMETIMES_NATIVE 和 Q_ATOMIC_INT32_REFERENCE_COUNTING_IS_NOT_NATIVE 都不会被定义。
           如果一个操作能在常量时间内完成,我们说它是wait-free。这类操作的实现不需要用到锁或者某种循环。并且,在Qt中,被平台一直支持的原子操作都是wait-free的。另外,Qt还定义了宏Q_ATOMIC_INTnn_OPERATION_IS_WAIT_FREE 来检测一个原子操作是否是wait-free的。
           除此之外,有些原子操作只能在较新的处理器上呗支持,所以,我们除了需要在编译时检测某个特性是否被支持外,在程序运行时也需要这种检测。所以,Qt除了提供上面的宏用于编译时检测,也提供了几个API用户在代码中进行运行时的检测,如 isReferenceCountingNative()、isTestAndSetNative()、isFetchAndStoreNative()、isFetchAndAddNative()。同时,也提供了对wait-free特性的检测函数,如isReferenceCountingWaitFree()、isTestAndSetWaitFree()、isFetchAndStoreWaitFree()、isFetchAndAddWaitFree()。
           Qt中的原子操作在不同的版本中是不同的,所以Qt处于对老版本的兼容性,规定不带nn的宏就等价于32-bit的宏。例如,Q_ATOMIC_INT_REFERENCE_COUNTING_IS_WAIT_FREE 等价于 Q_ATOMIC_INT32_REFERENCE_COUNTING_IS_WAIT_FREE。
           最后,上面只是从大的方面讲解了QAtomicInteger类的功能,至于其中的每个函数的具体使用也都是见名知意的,比如 ++、--、load、store等,在此就不一一讲解了,大家可以在用到时,参考Qt帮助文档即可。另外,Qt还提供了QAtomicInt 和 QAtomicPointer 类,用法与此类似。
---------------------
作者:求道玉
来源:CSDN
原文:https://blog.csdn.net/Amnes1a/article/details/62881888
版权声明:本文为博主原创文章,转载请附上博文链接!

Qt里的原子操作QAtomicInteger,有挑战性,使用Q_ATOMIC_INT{nn}_IS_SUPPORTED测试系统是否支持的更多相关文章

  1. Qt里的原子操作QAtomicInteger

    所谓原子操作,即一系列复杂的操作能一气呵成,中间不被其他的操作打断.这在多线程程序中尤其常见,但要实现这种功能,既要考虑程序的良好设计,又要关心特定平台的体系结构和相关编译器对原子特性的支持程度.所以 ...

  2. Qt里怎么处理二进制数据

    Qt里有个专门的类QDataStream就是专门读写二进制数据的, 它与QByteArray搭配在网络编程中有奇效. 来个栗子: // write data QByteArray data; QDat ...

  3. Qt里获取目录的一个另类方法

    如果有一个文件的全路径文件名, 想获取它的路径的话, qt里我没找到比较好的办法, 都是cleanPath后, 再用QString的find, left这种函数来处理. 今天又在搞这种问题的时候, 看 ...

  4. QT里使用sqlite的问题,好多坑

    1. 我使用sqlite,开发机上好好的,测试机上却不行.后来发现是缺少驱动(Driver not loaded Driver not loaded),代码检查了又检查,发现应该是缺少dll文件(系统 ...

  5. qt里标识操作系统的宏

    qt文件里的 windows的宏是Q_WS_WIN32/*   The window system, must be one of: (Q_WS_x) MACX     - Mac OS X      ...

  6. Qt里的slot

    昨天出了一个小bug, 一直调都没调出来, 今天仔细看了下, 发现出错的原因了. 我在用osgEarth的时候, 用到一个类MapCatalogWidget, 觉得它不够用, 就把这个类给改了下, 添 ...

  7. QT里嵌入Python

    刚看到一个软件,叫做,明明是QT做的,却带了很多pyd文件(Python编译后的文件),上网一查,果然有这套相关的东西: https://doc.qt.io/archives/qq/qq23-pyth ...

  8. 关于在Qt里让程序休眠一段时间的方法总结

    出处:http://hanzhaoxin.cnblogs.com/ Qt 为何没有提供 Sleep 论坛上不时见到有人问: Qt 为什么没有提供跨平台的 sleep 函数? 使用平台相关的 Sleep ...

  9. Using 3D engines with Qt(可以整合到Qt里,不影响)

    A number of popular 3D engines can be integrated with Qt: Contents [hide]  1 Ogre 2 Irrlicht 3 OpenS ...

随机推荐

  1. 4.linux vim 编辑器的基本操作

    一.vim编辑器基本操作       1.vim 编辑器 :  三种工作模式                              命令模式           a.i.o             ...

  2. this和super的用法

    this关键字的使用: 1.成员变量和局部变量重名时,在方法中调用成员变量,需要使用this.调用. 2.把这个类自己的实例化对象当做参数进行传递时,使用this. 3.内部类中,调用外部类的方法或变 ...

  3. 判断 js 类型的方式

    1. typeof 可以判断出'string','number','boolean','undefined','symbol'但判断 typeof(null) 时值为 'object'; 判断数组和对 ...

  4. PHP根据传入的经纬度,和距离范围,返回所有在距离范围内的经纬度的取值范围

    /** * 根据传入的经纬度,和距离范围,返回所有在距离范围内的经纬度的取值范围 * @param float $lng 经度 * @param float $lat 纬度 * @param floa ...

  5. i18n 语言码和对应的语言库

    语言码 语言名称 af Afrikaans am Amharic ar Arabic az Azerbaijani be Belarusian bg Bulgarian bh Bihari bn Be ...

  6. 原生app是什么意思?

    原生的就是用 Android 和ios 写的 完全符合手机系统 其他的都是通过各种工具对代码转换为手机系统可以识别

  7. ios排序NSArray(数字.字符串)

    NSArray *originalArray = @[@"1",@"21",@"12",@"11",@"0&q ...

  8. springboot配置虚拟路径访问用户上传的附件及图片资源

    在springmvc项目中,我们通常把图片及附件存放到WEB-INF/upload类似的路径. springboot项目是通过jar包方式运行的. 笔者曾尝试以下代码,把图片转成base64格式的图片 ...

  9. thinkphp3.2.2 没有定义数据库配置

    出现这个问题,温习下tp配置多个数据库 <?php return array( //默认数据库 'DB_TYPE' => 'mysql', // 数据库类型 'DB_HOST' => ...

  10. 心の壁 From EOE:Air / 真心为你

    https://www.bilibili.com/bangumi/media/md10272/ 真嗣:我问你. 凌波:什么? 真嗣:梦到底是什么呢? 凌波:梦? 真嗣:我不太明白什么是现实. 凌波:你 ...