首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

浅墨: 聊聊原子变量、内存屏障那点事(1)

突然想聊聊这个话题,是因为知乎上的一个问题多次出现在了我的Timeline里:请问,多个线程可以读一个变量,只有一个线程可以对这个变量进行写,到底要不要加锁?...早在单核时代,使用或者原子变量就很容易达成这一目的。甚至因为CPU的一些访存特性,对某些内存对齐数据的读或写也具有原子的特性。...也就是说,有些内存对齐的数据的访问在CPU层面就是原子进行的(注意这里说的只是单次的读或者写,类似普通变量i的i++操作不止一次内存访问)。...注意这里我加粗了单核CPU这个关键字,那么到了多核心处理器的今天,该操作就不是原子了吗?不,依旧是原子的,但是出现了其他的干扰因素迫使可能需要额外的同步措施才能保证原本无代码的正确运行。...简单总结一下,如果不做多余的防护措施,单核时代的无环形队列在多核CPU中,一个CPU核心上的Writer写入数据,更新index后。

1.3K30
  • 您找到你想要的搜索结果了吗?
    是的
    没有找到

    浅墨: 聊聊原子变量、内存屏障那点事(2)

    IA-32/AMD64架构上,在Linux下常用的GCC编译器上,优化屏障定义为(linux kernel, include/linux/compiler-gcc.h): 优化屏障告知编译器: 1....同时如果是32-bit的变量则P6之前的CPU还要保持32-bit字节对齐,如果是64-bit变量在IA-32上无法保障(IA-32下64bit的变量Store操作不是原子的)。...下面说说原子变量。对于数据竞争(Data Races)的情况,最简单和最常见的场景就是使用Mutex了,包括并不限于互斥、自旋、读写等。...与之类似,平台提供的原子变量除了保证内存操作原子之外,也会保证访存的一致性。...至于工程上,普通的程序员老老实实的用Mutex就好了,普通的计数类场景用原子变量也无可厚非。

    1.5K40

    Linux内核中的各种:信号量互斥读写原子自旋内存屏障等

    下面分别是作用于临界区、CPU、内存、cache的各种的归纳: 一、atomic原子变量/spinlock自旋 — —CPU 既然是CPU,那就都是针对多核处理器或多CPU处理器。...原子变量:在x86多核环境下,多核竞争数据总线的时候,提供Lock指令锁住总线,保证“读-修改-写”操作在芯片级的原子性。...使用实例如下: #include // 定义自旋 spinlock_t my_lock; void my_function(void) { spin_lock...线程的上下文切换主要是线程栈、寄存器、线程局部变量等。 而自旋锁在当前线程获取失败时不会进行线程的切换,而是一直循环等待直到获取成功。因此,自旋不会切换至内核态,也没有线程切换开销。...而且,实际上很多线程同步机制,都在底层有内存屏障作为支撑,比如原子和自旋都是依赖CPU提供的CAS操作实现。

    1.1K10

    并发基础之原子操作与原子变量

    虽然这个方法可以解决问题,但大家可以自己试一下,你会发现加锁之后性能急剧下降,主要原因是冲突会导致线程上下文切换,这种切换开销很大。 下面我们来试试使用原子变量。...如果我们运行这段代码,会发现它比前面提到的加锁方法效率高很多,加锁方法执行1亿次加法所用时间是使用原子变量的好几倍。为什么使用原子变量效率会高出这么多呢?...{ i = i + 1; break; } unlock总线 //lock cmpxchg指令的的逻辑 ---END--- } 可以清楚的看到原子变量原子操作其实也使用了...,只不过这个是硬件指令级别的,比我们软件实现的高效得多,更重要的是从上面的伪码可以看出,如果出现了冲突,只是不停的循环重试,而不会切换线程。...最后简单的总结一下Java以及gcc对原子变量的实现:Java中用的是循环使用CAS操作实现的原子变量原子操作,而gcc使用的是xadd指令,可以看出gcc的实现方式更加简洁,应该也更高效,另外,go

    1.5K20

    CAS乐观原子操作)

    IDEA 注册码,2020.2 IDEA 激活码 CAS乐观原子操作) 主要分为两种:乐观和悲观,而 synchronized 就属于一种悲观,每次在操作数据前都会加锁。...即使变量被 volatile 修饰,但可以用原子方式 AtomicInteger 自增,这样可以保证数据的原子性。...这是一中完全依赖于硬件的功能,通过它实现原子操作。 【3】进入Unsafe 类的 getAndAddInt 方法:我们发现其通过无限循环去解决的问题,也称为 “循环”,直到修改成功。...【2】**只能保证一个共享变量原子操作:**只能保证一个共享变量原子操作。...当对一个共享变量执行操作时,我们可以使用循环 CAS 的方式来保证原子操作,但是对多个共享变量操作时,循环 CAS 就无法保证操作的原子性,这个时候就可以用,或者有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作

    1.2K30

    jdk源码分析之原子变量AtomicInteger--乐观与CAS以及问题

    )等都是悲观,而我们常见的并发包中原子变量原子变量引用(java.util.concurrent.atomic包下类)以及开源分布式缓存框架Tair都是基于乐观实现。...关于悲观和乐观此篇不做详细对比和分析,接下来借助原子变量AtomicInteger展开话题。 首先先从看一下类的声明和介绍: ?...然后我们根据具体的源码实现分析AtomicInteger中的乐观和CAS以及原子变量存在的一些问题: public AtomicInteger(int initialValue) { value...这就是典型的原子变量中的ABA问题,其实这种简单的操作带来的问题不大,但是在真实的业务场景中ai的值的版本号已经发生了变化,那么这种问题如何解决呢?...此篇我们队Jdk集合中原子变量AtomicInteger以及常用实现做了详细的分析,希望在实际开发中给大家带来帮助和源码层面实现思路的理解。

    33820

    信号量、互斥、自旋原子操作

    linux内核中有多种内核,内核的作用是: 多核处理器下,会存在多个进程处于内核态的情况,而在内核态下,进程是可以访问所有内核数据的,因此要对共享数据进行保护,即互斥处理; linux内核机制有信号量...4.2、Linux内核两组原子操作接口: 1、原子整数操作 原子操作通常针对int或bit类型的数据,但是Linux并不能直接对int进行原子操作,而只能通过atomic_t的数据结构来进行。...(0); //定义原子变量v,并初始化为0; 3 4 atomic_read(atomic_t* v); //返回原子变量v的值; 5 6 void atomic_add(int...为 1 ,将原子变量减 一 为 0 ,函数返回 true ,再 !...true 为 假 ,if 里面的代码不执行;这样打开了、并使用该驱动, 原子变量变为 0; 如果再有进程来打开驱动程序,0-1 = 负1,返回 false ,if 条件成立,运行里面的代码,将原子变量加一恢复到

    3.1K40

    C 语言的 互斥、自旋原子操作

    本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金 今天不整 GO 语言,我们来分享一下以前写的 C 代码,来看看 互斥,自旋原子操作的 demo 互斥 临界区资源已经被1个线程占用...函数中创建 10 个线程 线程函数中调用 inc 做数据的增加 分别使用 互斥,自旋,和原子操作,来进行控制 #include #include #include...自旋或者是原子操作看看效果进行对比一下 2、mutex、lock、atomic 性能对比 思路还是和上面的思路类型,咱们可以通过下面的代码来实际初步看看 mutex、lock、atomic 各自的性能...//并发 //互斥mutex // 如果获取不到资源会让出cpu // 使用场景 // 共享区域执行的内容较多的情况 //自旋spinlock // 如果获取不到资源,会原地自旋,忙等 // 使用场景...自旋原子操作,数据都能如我所愿的累加正确,在时间上面他们还是有一定的差异: 自旋 和 互斥 在此处的案例性能差不多,但是原子操作相对就快了很多 欢迎点赞,关注,收藏 朋友们,你的支持和鼓励,是我坚持分享

    1.2K20

    Java并发编程之原子变量

    原子变量的粒度更细,量级更轻,并且对于在多处理器系统上实现高性能的并发代码来说是非常关键的。 原子变量类相当于一种泛化的 volatile 变量,能够支持原子的和有条件的读-改-写操作。...原子类在内部使用现代 CPU 支持的 CAS 指令来实现同步。这些指令通常比更快。 原子更新基本类型 AtomicBoolean - 原子更新布尔类型。...AtomicInteger - 原子更新整型。 AtomicLong - 原子更新长整型。...AtomicLongArray - 原子更新长整型数组里的元素。 AtomicReferenceArray - 原子更新引用类型数组的元素。...该类将整型数值与引用关联起来,可用于原子的更新数据和数据的版本号,可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。

    27620

    Java并发编程之原子变量

    原子变量最主要的一个特点就是所有的操作都是原子的,synchronized关键字也可以做到对变量原子操作。...只是synchronized的成本相对较高,需要获取对象,释放对象,如果不能获取到,还需要阻塞在阻塞队列上进行等待。而如果单单只是为了解决对变量原子操作,建议使用原子变量。...关于原子变量的介绍,主要涉及以下内容: 原子变量的基本概念 通过AtomicInteger了解原子变量的基本使用 通过AtomicInteger了解原子变量的基本原理 AtomicReference的基本使用...我们先看一段synchronized关键字保证变量原子性的代码: ? 简单的count++操作,线程对象首先需要获取到Counter 类实例的对象,然后完成自增操作,最后释放对象。...整个过程中,无论是获取还是释放都是相当消耗成本的,一旦不能获取到,还需要阻塞当前线程等等。

    34030

    Java并发编程-原子变量

    原子性布尔 AtomicBoolean AtomicBoolean 类为我们提供了一个可以用原子方式进行读和写的布尔值,它还拥有一些先进的原子性操作,比如 compareAndSet()。...compareAndSet() 方法是原子性的,因此在同一时间之内有单个线程执行它。因此 compareAndSet() 方法可被用于一些类似于的同步的简单实现。...原子性整型 AtomicInteger AtomicInteger 类为我们提供了一个可以进行原子性读和写操作的 int 变量,它还包含一系列先进的原子性操作,比如 compareAndSet()。...原子性长整型 AtomicLong AtomicLong 类为我们提供了一个可以进行原子性读和写操作的 long 变量,它还包含一系列先进的原子性操作,比如 compareAndSet()AtomicLong...原子性引用型 AtomicReference AtomicReference 提供了一个可以被原子性读和写的对象引用变量

    2.4K30

    Java并发编程之原子变量

    原子变量最主要的一个特点就是所有的操作都是原子的,synchronized关键字也可以做到对变量原子操作。...只是synchronized的成本相对较高,需要获取对象,释放对象,如果不能获取到,还需要阻塞在阻塞队列上进行等待。而如果单单只是为了解决对变量原子操作,建议使用原子变量。...关于原子变量的介绍,主要涉及以下内容: 原子变量的基本概念 通过AtomicInteger了解原子变量的基本使用 通过AtomicInteger了解原子变量的基本原理 AtomicReference的基本使用...使用FieldUpdater操作非原子变量的字段属性 经典的ABA问题的解决 一、原子变量的基本概念      原子变量保证了该变量的所有操作都是原子的,不会因为多线程的同时访问而导致脏数据的读取问题...整个过程中,无论是获取还是释放都是相当消耗成本的,一旦不能获取到,还需要阻塞当前线程等等。

    73990

    全面了解 Java 原子变量

    一、原子变量类简介 为何需要原子变量类 保证线程安全是 Java 并发编程必须要解决的重要问题。Java 从原子性、可见性、有序性这三大特性入手,确保多线程的数据一致性。...volatile 是轻量级的(自然比普通性能要好),它保证了共享变量在多线程中的可见性,但无法保证原子性。所以,它只能在一些特定场景下使用。...为了兼顾原子性以及带来的性能问题,Java 引入了 CAS (主要体现在 Unsafe 类)来实现非阻塞同步(也叫乐观)。并基于 CAS ,提供了一套原子工具类。...原子变量类的作用 原子变量类 比的粒度更细,更轻量级,并且对于在多处理器系统上实现高性能的并发代码来说是非常关键的。原子变量将发生竞争的范围缩小到单个变量上。...原子变量类相当于一种泛化的 volatile 变量,能够支持原子的、有条件的读/改/写操作。 原子类在内部使用 CAS 指令(基于硬件的支持)来实现同步。这些指令通常比更快。

    80410

    Linux】线程安全——补充|互斥、|同步、条件变量

    Linux并不提供真正的线程,只提供了LWP,但是程序员用户不管LWP,只要线程。...实际上对变量做–的时候,对应三条汇编语句,未来会对应三条汇编语句!所以很明显,++、–不是原子性的,不是一条语句。 单纯的++或者++都不是原子的,有可能会有数据一致性的问题。...实际上就是需要一把Linux提供的这把就叫互斥量,如果一个线程持有,那么其他的线程就无法进来访问了。...加锁的过程其实是原子的 谁持有,谁就进入临界区!...互斥实现原子性原理 单纯的i++,++i都不是原子的,会导致数据不一致问题 从汇编谈加锁:为了实现互斥操作,大多数体系结构提供了swap和exchange指令,作用是把寄存器和内存单元的数据直接做交换

    27120

    GO的原子操作分享

    举一个编码中的例子 package main import ( "fmt" "sync" ) // 全局变量 var num int64 var wg sync.WaitGroup func...20000000,每一个协程计算 10000000 次,可是实际结果却是 10378923 每一次计算的结果还不一样,出现这个问题的原因就是上述提到的资源竞争 两个 goroutine 协程在访问和修改num变量..."fmt" "sync" ) // 全局变量 var num int64 var wg sync.WaitGroup var lock sync.Mutex func add() { for...加互斥 互斥的 add函数 是并发安全的,因为拿不到互斥会阻塞,所以加锁性能开销大 20000 14.9586ms 使用原子操作 原子操作的 add函数 是并发安全,性能优于加锁的 20000...9.9726ms 总结 分享了是什么,用来做什么 分享了互斥,读写,以及其区别和应用场景 分享了原子操作 大家感兴趣可以去看看的实现,里面也是有使用原子操作 欢迎点赞,关注,收藏 朋友们

    30630
    领券