java 加减乘除是原子操作吗_聊聊原子操作那些事

原子操作,线程间交互数据最细粒度的同步操作,它可以保证线程间读写某个数值的原子性。

由于不需要加重量级的互斥锁进行同步,因此非常轻量,而且也不需要在内核间来回切换调度,效率是非常高的。。

那如何使用原子操作了,各个平台下都有相关api提供了支持,并且向gcc、clang这些编译器,也提供了编译器级的__builtin接口进行支持

windows的Interlockedxxx和Interlockedxxx64系列api

macosx的OSAtomicXXX系列api

gcc的__sync_val_compare_and_swap和__sync_val_compare_and_swap_8等__builtin接口

x86和x86_64架构的lock汇编指令

tbox的跨平台原子接口

tbox接口使用

先拿tbox的tb_atomic_fetch_and_add接口为例,顾名思义,这个api会先读取原有数值,然后在其基础上加上一个数值:

// 相当于原子进行:b = *a++;

tb_atomic_t a = 0;

tb_long_t b = tb_atomic_fetch_and_add(&a, 1);

如果需要先进行add计算,再返回结果可以用:

// 相当于原子进行:b = ++*a;

tb_atomic_t a = 0;

tb_long_t b = tb_atomic_add_and_fetch(&a, 1);

或者可以更加简化为:

tb_long_t b = tb_atomic_fetch_and_inc(&a);

tb_long_t b = tb_atomic_inc_and_fetch(&a);

那tbox在内部如何去适配各个平台的呢,我们可以简单看下,基本上就是对原生api进行了一层wrap而已。

windows接口封装

static __tb_inline__ tb_long_t tb_atomic_fetch_and_add_windows(tb_atomic_t* a, tb_long_t v)

{

return (tb_long_t)InterlockedExchangeAdd((LONG __tb_volatile__*)a, v);

}

static __tb_inline__ tb_long_t tb_atomic_inc_and_fetch_windows(tb_atomic_t* a)

{

return (tb_long_t)InterlockedIncrement((LONG __tb_volatile__*)a);

}

gcc接口的封装

static __tb_inline__ tb_long_t tb_atomic_fetch_and_add_sync(tb_atomic_t* a, tb_long_t v)

{

return __sync_fetch_and_add(a, v);

}

x86和x86_64架构汇编实现

static __tb_inline__ tb_long_t tb_atomic_fetch_and_add_x86(tb_atomic_t* a, tb_long_t v)

{

/*

* xaddl v, [a]:

*

* o = [a]

* [a] += v;

* v = o;

*

* cf, ef, of, sf, zf, pf... maybe changed

*/

__tb_asm__ __tb_volatile__

(

#if TB_CPU_BITSIZE == 64

"lock xaddq %0, %1 \n" //!< xaddq v, [a]

#else

"lock xaddl %0, %1 \n" //!< xaddl v, [a]

#endif

: "+r" (v)

: "m" (*a)

: "cc", "memory"

);

return v;

}

原子操作除了可以进行对int32和int64数值加减乘除外,还可以进行xor, or, and等逻辑计算,用法类似,这里就不多说了。

下面我们再来个简单的实例,来实际运用下,原子的应用场景还是蛮多的,比如:

用于实现自旋锁

用于实现无锁队列

线程间的状态同步

用于实现单例

等等。。

自旋锁的实现

我们先来看下如何去实现一个简单的自旋锁,为了统一规范演示代码,下面的代码都用tbox提供的原子接口为例:

static __tb_inline_force__ tb_bool_t tb_spinlock_init(tb_spinlock_ref_t lock)

{

// init

*lock = 0;

// ok

return tb_true;

}

static __tb_inline_force__ tb_void_t tb_spinlock_exit(tb_spinlock_ref_t lock)

{

// exit

*lock = 0;

}

static __tb_inline_force__ tb_void_t tb_spinlock_enter(tb_spinlock_ref_t lock)

{

/* 尝试读取lock的状态值,如果还没获取到lock(状态0),则获取它(设置为1)

* 如果对方线程已经获取到lock(状态1),那么循环等待尝试重新获取

*

* 注:整个状态读取和设置,是原子的,无法被打断

*/

tb_size_t tryn = 5;

while (tb_atomic_fetch_and_pset((tb_atomic_t*)lock, 0, 1))

{

// 没获取到lock,尝试5次后,还不成功,则让出cpu切到其他线程运行,之后重新尝试获取

if (!tryn--)

{

// yield

tb_sched_yield();

// reset tryn

tryn = 5;

}

}

}

static __tb_inline_force__ tb_void_t tb_spinlock_leave(tb_spinlock_ref_t lock)

{

// 释放lock,此处无需原子,设置到一半被打断,数值部位0,对方线程还是在等待中,不收影响

*((tb_atomic_t*)lock) = 0;

}

这个实现非常简单,但是tbox里面,基本上默认都是在使用这个spinlock,因为tbox里面大部分多线程实现,粒度都被拆的很细

大部分情况下,用自旋锁就ok了,无需进入内核态切换等待。。

使用方式如下:

// 获取lock

tb_spinlock_enter(&lock);

// 一些同步操作

// ..

// 释放lock

tb_spinlock_leave(&lock);

上面的代码中,省略了init和exit操作,实际使用时,在响应初始化和释放的地方,做相应处理下就行了。。

类pthread_once的实现

pthread_once 可以在多线程函数内,可以保证传入的函数只被调用到一次,一般可以用来初始化全局单例或者TLS的key初始化

以tbox的接口为例,我先来来看下,这个函数的使用方式:

// 初始化函数,只会被调用到一次

static tb_void_t tb_once_func(tb_cpointer_t priv)

{

// 初始化一些单例对象,全局变量

// 或者执行一些初始化调用

}

// 线程函数

static tb_int_t tb_thread_func(tb_cpointer_t priv)

{

// 全局存储lock,并初始化为0

static tb_atomic_t lock = 0;

if (tb_thread_once(&lock, tb_once_func, "user data"))

{

// ok

}

}

我们这里拿原子操作,可以简单模拟实现下这个函数:

tb_bool_t tb_thread_once(tb_atomic_t* lock, tb_bool_t (*func)(tb_cpointer_t), tb_cpointer_t priv)

{

// check

tb_check_return_val(lock && func, tb_false);

/* 原子获取lock的状态

*

* 0: func还没有被调用

* 1: 已经获取到lock,func正在被其他线程调用中

* 2: func已经被调用完成,并且func返回ok

* -2: func已经被调用,并且func返回失败failed

*/

tb_atomic_t called = tb_atomic_fetch_and_pset(lock, 0, 1);

// func已经被其他线程调用过了?直接返回

if (called && called != 1)

{

return called == 2;

}

// func还没有被调用过?那么调用它

else if (!called)

{

// 调用函数

tb_bool_t ok = func(priv);

// 设置返回状态

tb_atomic_set(lock, ok? 2 : -1);

// ok?

return ok;

}

// 正在被其他线程获取到lock,func正在被调用中,还没完成?尝试等待lock

else

{

// 此处简单的做了些sleep循环等待,直到对方线程func执行完成

tb_size_t tryn = 50;

while ((1 == tb_atomic_get(lock)) && tryn--)

{

// wait some time

tb_msleep(100);

}

}

/* 重新获取lock的状态,判断是否成功

*

* 成功:2

* 超时:1

* 失败:-2

*

* 此处只要不是2,都算失败

*/

return tb_atomic_get(lock) == 2;

}

64位原子操作

64位操作跟32位的接口使用方式,是完全一样的,仅仅只是变量类型的区别:

tbox中类型为tb_atomic64_t,接口改为tb_atomic64_xxxx

gcc中类型为volatile long long,接口改为__sync_xxxx_8系列

windows上则为Interlockedxxx64

具体使用方式参考32位,这里就不详细介绍了。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/303423.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

有关技术管理的一些思考

这些天里工作的环境发生了一些微小的变化&#xff0c;可能以后对基层开发的程序员也会有更加具体的影响。上周参加 Open Party 时&#xff0c;重点听了《那些失败的项目们》&#xff0c;分析了一个项目的提出、实施&#xff0c;直到最后失败的过程。我也在想一个技术团队究竟应…

15个未来高科技产品会让你无法想象!这些开脑洞的设计太牛了!

导读&#xff1a;从衣食住行到生活的方方面面&#xff0c;未来必将会有天翻地覆的变化。大数据、云计算、物联网和人工智能这些年的发展&#xff0c;让我们对并不遥远的未来有了更多想象和期待。那些我们现阶段不可企及的所思所想&#xff0c;将在未来成为大部分人的日常。这么…

解决 .NET Core 在 Linux Container 中获取 CurrentCulture 不正确的问题

背景在将公司一款基于 .NET Framework 的控制台程序迁移到 .NET Core 3.1 时&#xff0c;发现程序中本地化的部分失效&#xff0c;症状类似于对 Thread.CurrentThread.CurrentCulture.Name 的值进行 Substring() 操作时抛出 ArgumentOutOfRangeException 异常。该程序在 Window…

Java程序设计当中包的使用_【学习笔记】 唐大仕—Java程序设计 第4讲 类、包和接口之4.2 类的继承...

【学习笔记】 唐大仕—Java程序设计 第4讲 类、包和接口之4.2 类的继承super的使用1.使用super访问父类的域和方法注意&#xff1a;正是由于继承&#xff0c;使用this可以访问父类的域和方法。但是有时为了明确指明父类的域和方法&#xff0c;就要用关键字super。this和super都…

机器学习资料升级版来了!!!

机器学习一直是一个热门的领域。上次分享的机器学习资料【资源】机器学习资料包来袭受到大家的广泛好评&#xff0c;今天小编打算分享一份机器学习升级版的资料&#xff0c;有斯坦福大学-深度学习基础教程、机器学习实战、人工智能与大数据、Tagging等。顺序最下优化算法Error …

java consumer 使用_java.util.function.Consumer的用法

Consumer类包含两个方法&#xff0c;一个accept方法用来对输入的参数进行自定义操作&#xff0c;因为是个抽象方法&#xff0c;所以需要实例化对象的时候进行Override&#xff0c;另一个andThen方法跟Function的方法一样是一个default方法&#xff0c;已经有内部实现所以不需要…

程序员从初级到中级10个秘诀

Justin James曾发表过一篇博文《10 tips for advancing from a beginner to an intermediate developer》&#xff0c;为我们分享如何才能完成程序员从初级到中级的蜕变&#xff0c;现将中文译文转载于此&#xff0c;供大家借鉴。 在一封与TechRepublic会员交流的邮件当中&…

C# 可空类型

C# 单问号 ? 与 双问号 ?? ? : 单问号用于对 int,double,bool 等无法直接赋值为 null 的数据类型进行 null 的赋值&#xff0c;意思是这个数据类型是 Nullable 类型的。int? i 3;等同于&#xff1a;Nullable<int> i new Nullable<int>(3); int i; //默认值0…

K-Means算法的10个有趣用例

K-means算法具有悠久的历史&#xff0c;并且也是最常用的聚类算法之一。K-means算法实施起来非常简单&#xff0c;因此&#xff0c;它非常适用于机器学习新手爱好者。首先我们来回顾K-Means算法的起源&#xff0c;然后介绍其较为典型的应用场景。起源1967年&#xff0c;James M…

计算数字的出现次数 java_关于Java:如何计算数字在.txt文件中出现的次数

早上好&#xff0c;我正在做一些Java练习&#xff0c;偶然发现了这个问题。 我有一个.txt文件&#xff0c;其中包含多个数字&#xff0c;每行一个。 练习的目的是查看哪些数字等于10 ^ 0&#xff0c;...&#xff0c;10 ^ n直到达到n。 然后&#xff0c;我必须在.txt中写入每个数…

Asp.net中的HttpModule和HttpHandler的简单用法

在Asp.net中&#xff0c;HttpModule和HttpHandler均可以截取IIS消息进行处理&#xff0c;这使得我们制作人员能够非常方便的进行诸如图片水印添加&#xff0c;图片盗链检查等功能。 下面先就HttpModule的使用方法进行简单说明&#xff1a; using System;using System.Web;names…

C# $的用法

今天闲来无事&#xff0c;就随便在网上乱看&#xff0c;突然想到Jquery的$符号很强大&#xff0c;那么C#有没有这个东西呢&#xff0c;一查&#xff0c;果然有。经查证发现&#xff0c;这个是在C#6.0出现的一个新特性&#xff0c;也就是一个小语法糖&#xff0c;其作用相当于对…

想转行人工智能?机会来了!!!

一个坏消息&#xff1a;2018年1月 教育部印发的《普通高中课程方案和语文等学科课程标准》新加入了数据结构、人工智能、开源硬件设计等 AI 相关的课程。这意味着职场新人和准备找工作的同学们&#xff0c;为了在今后十年内不被淘汰&#xff0c;你们要补课了&#xff0c;从初中…

tankwar java_TankWar 单机(JAVA版) 版本0.3 画出坦克

其实就是通过自定义的panel 重写里面的paint方法 使用Graphics类画一个圆然后把自定的panel添加到窗口中由于想到tank不能只画一个 所以我们封装一个tank类 里面有一个draw方法 用来画 坦克通过实例化tank 调用draw方法就能实现画tank了具体代码如下&#xff1a;Tank类pac…

轻量NuGet服务—BaGet

相信大家都受益过nuget.org&#xff0c;上面的海量的库、工具、模板为我们开发提供了极大的帮助&#xff0c;其中有很多都是非常宽松的开源协议&#xff0c;在此感谢那无私奉献的人。有的时候&#xff0c;在企业内部&#xff0c;有些库是私有的&#xff0c;专项的&#xff0c;要…

光驱读盘不正常

问&#xff1a;我的电脑安装的是Windows XP操作系统&#xff0c;安装好后出现了一个问题&#xff0c;就是不能连续读光盘&#xff0c;插入第一张光盘一切正常&#xff0c;打开光驱放入第二张光盘&#xff0c;发现里边的内容竟是第一张光盘的内容&#xff0c;重新启动机器后&…

在安卓上安装java_如何在Android上安装JavaCV并使用FrameGrabber

有人能告诉我我在哪里做错了吗&#xff1f;这些是我遵循的步骤&#xff1a;>从Android开发者网站下载了adt-bundle-windows>创建了一个新项目和一个libs / armeabi文件夹>将所有* .so文件从javacv-android-arm.jar,opencv-2.4.3-android-arm.zip和ffmpeg-1.0-android-…

使用 Github Actions artifact 在 workflow job 之间共享数据

AgileConfig 在使用 react 编写UI后&#xff0c;变成了一个彻彻底底的前后端分离的项目&#xff0c;上一次解决了把react spa 跟asp.net core 站点集成起来ASP.NET Core 集成 React SPA 应用。本来我每次提交代码的时候都需要手动运行npm run build,然后把dist的内容复制到asp…

撤回的微信消息真的看不到?78行Python代码帮你看穿一切!

导读&#xff1a;Python曾经对我说&#xff1a;"时日不多&#xff0c;赶紧用Python"。于是看到了一个基于python的微信开源库&#xff1a;itchat&#xff0c;玩了一天&#xff0c;做了一个程序&#xff0c;把私聊撤回的信息可以收集起来并发送到个人微信的文件传输助…

WCF的用户名+密码认证方式

概述 今天在做Master Data Service&#xff08;后面简称MDS&#xff09;项目时需要通过WCF来使用MDS的API&#xff0c;从而对MDS的数据进行操作。在这个过程中&#xff0c;遇到了一个棘手的问题&#xff0c;就是在客户端调用Web Service时的身份认证问题&#xff0c;于是乎对WC…