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

  1. Qt里的原子操作QAtomicInteger,有挑战性,使用Q_ATOMIC_INT{nn}_IS_SUPPORTED测试系统是否支持

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

  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. Failed to Stop or Restart Nginx Server Through Serevice Command(nginx进程不能停止重启)

    Many people are accustomed to start a Nginx web server through init scripts and then they can contro ...

  2. sql分组最大值相关

    房产表tf_estate_card,利润中心组profit_group_code,资产号main_assets_number,原值original_value 查出每个利润中心组的最大原值及其资产号 ...

  3. C#创建一个Window服务

    Window服务介绍 Microsoft Windows 服务能够创建在它们自己的 Windows 会话中可长时间运行的可执行应用程序.这些服务可以在计算机启动时自动启动,可以暂停和重新启动而且不显示 ...

  4. Win7与虚拟机Linux互通ping的网络设置

    转载请标明出处:http://www.linuxidc.com/Linux/2014-04/100450.htm 虽然从WinXP到Win7一直都可以使用VMWARE虚拟机安装Linux系统,记得每次 ...

  5. html table表格导出excel的方法 html5 table导出Excel HTML用JS导出Excel的五种方法 html中table导出Excel 前端开发 将table内容导出到excel HTML table导出到Excel中的解决办法 js实现table导出Excel,保留table样式

    先上代码   <script type="text/javascript" language="javascript">   var idTmr; ...

  6. Python pip install Twisted 出错“Command "c:\python37\python.exe -u -c "import setuptools, tokenize;__file__='C:...\\Twisted\\setup.py'.... failed with error code 1 in C:... \\Twisted"

    如标题所说: python版本是目前最新的3.7.1 结果发现并不是环境问题,而是直接 pip install Twisted 安装的包不兼容 需要手动下载兼容的扩展包Twisted-18.9.0-c ...

  7. es redis logstash 日志收集系统排错

    用logstash收集日志并发送到redis,然后通过logstash取redis数据写入到es集群,最近kibana显示日志总是中断,日志收集不过来,客户端重启发现报错: Failed to sen ...

  8. POJ-3134-Power Calculus(迭代加深DFS)

    Description Starting with x and repeatedly multiplying by x, we can compute x31 with thirty multipli ...

  9. Android中图片的三级缓存策略

    在开发过程中,经常会碰到进行请求大量的网络图片的样例.假设处理的不好.非常easy造成oom.对于避免oom的方法,无非就是进行图片的压缩.及时的回收不用的图片.这些看似简单可是处理起来事实上涉及的知 ...

  10. VmWare下安装CentOS6

    为什么选择CentOS ? 1. 主流: 目前的Linux操作系统主要应用于生产环境,主流企业级Linux系统仍旧是RedHat或者CentOS 2. 免费: RedHat 和CentOS差别不大,C ...