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

    最近两个月除了必要的工作外,其余时间都在干一些我其实平时很少干的事, 喝酒.唱歌.打麻将.玩牌.以及到处跑找朋友玩,也许是过年的原因我放纵了自己,也许是自己心中的烦恼.我的博客记录着我每次看书学习的笔 ...

  2. 【精】iOS GCD 具体解释

    一.介绍 1.什么是GCD? Grand Central Dispatch.是苹果公司开发的一套多核编程的底层API. GCD首次公布在Mac OS X 10.6,iOS4及以上也可用.GCD存在于l ...

  3. Java中super的几种使用方法并与this的差别

    1.     子类的构造函数假设要引用super的话,必须把super放在函数的首位. class Base { Base() { System.out.println("Base" ...

  4. Apache Rewrite 规则详解

    在开篇之前: 我想说这篇文章其实是我刚刚接触Rewrite的时候学习的文档,应属转载,但是在这里我不想写明原地址,原因是文章中大多数给出的配置命令经实验都是错误的.需要原文的可以在谷歌上搜索一下&qu ...

  5. sublime text 3 修改侧边栏字体

    安装PackageResourceViewer快捷键 Ctrl+Shift+P 打开 Command Palette 输入 Package Control:Install 回车, 等待加载packag ...

  6. Failed to import package with error: Couldn't decompress package

    解压unitypackage的时候出错.原因是路径中包括中文字符,更改成英文路径就可以. 參考 Error while importing package: Couldn't decompress p ...

  7. Linux管理员必须知道的sudo命令

    "Sudo"是Unix/Linux平台上的一个很实用的工具,它同意系统管理员分配给普通用户一些合理的"权利",让他们执行一些仅仅有超级用户或其它 特许用户才干完 ...

  8. Notes from Data Guard

    There are two types of Standby databases: 1, Physical standby database block-for-block basis the phy ...

  9. cmpp 短信平台

    背景: 物联网一般是在设备上安装sim卡,通过2g网络来进行设备与云端系统的交互,网络都是通过移动的基站来进行网络传输的,所以一旦移动的基站有变动,比如流量降级,光缆割接,其他故障登 都会导致2g络的 ...

  10. typescript 接口的新认识

    interface 用于接收服务器的数据. eg: interface mmmmm { x: string, y: number, z: number, select: KnockoutObserva ...