所谓原子操作,即一系列复杂的操作能一气呵成,中间不被其他的操作打断。这在多线程程序中尤其常见,但要实现这种功能,既要考虑程序的良好设计,又要关心特定平台的体系结构和相关编译器对原子特性的支持程度。所以,为了简化这个过程,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. IdentityServer4入门三:授权模式

    在入门一.入门二我们实现了一个完整的API保护的过程.需要保护的API只需在其Controler上应用[Authorize]特性,来显式指定受保护的资源.而我们实现的这个例子,所应用的模式叫“Clie ...

  2. golang ssh 远程执行命令(有一些命令会报command not found)

    func sshSession(user, password, host string, port int) (sshSession *ssh.Session, err error) { //参数: ...

  3. APP手工项目02-用例编写-测试报告-fiddler弱网测试

    回顾 APP专项测试(兼容性,安装,卸载,升级,交叉事件,PUSH消息,性能,其他类型) 项目环境(开发环境,测试环境,准生产环境,生产环境) APP内测发布平台(蒲公英,fir.im)支持apk,i ...

  4. 123457123456#0#-----com.twoapp.KidsShiZi01--前拼后广--儿童宝宝识字jiemei

    com.twoapp.KidsShiZi01--前拼后广--儿童宝宝识字jiemei

  5. spring redistemplate中setHashValueSerializer的设置

    笔者曾经对redis键值使用了不同类型的序列化方法 用过默认值.JdkSerializationRedisSerializer.StringRedisSerializer还用改以下自定类型的序列化工具 ...

  6. caffe dropout解读

    先上caffe dropout_layer.cpp源码,如下: // LayerSetUp DCHECK(threshold_ > 0.); DCHECK(threshold_ < 1.) ...

  7. 《Fluid Engine Development》 学习笔记2-基础

    断断续续花了一个月,终于把这本书的一二两章啃了下来,理解流体模拟的理论似乎不难,无论是<Fluid Simulation for Computer Graphics>还是<计算流体力 ...

  8. go context源码解析

    go 的context贯穿整个goroutine的运行控制的中枢,可以实现执行的生命周期的控制. Context是一个接口,他派生了context.emptyCtx(TODO),cancelCtx,t ...

  9. Andrew Ng机器学习课程16

    Andrew Ng机器学习课程16 声明:引用请注明出处http://blog.csdn.net/lg1259156776/ 说明:主要介绍了ICA算法,并利用最大似然估计和随机梯度上升算法进行求解, ...

  10. (生鲜项目)07. api view实现商品列表页

    第一步: 环境配置 1. DRF官网: https://www.django-rest-framework.org/ 仔细查看自己当前的python版本以及django版本是否支持DRF, 然后就看看 ...