goland -sync/atomic原子操作小结

目录
  • 1.go已经提供了锁,为什么还需要atomic原子操作?
  • 2.atomic原子操作为什么比mutex快?
  • 3.CAS
  • 4.互斥锁与原子操作区别
  • 5.原子操作方法
    • 5.1 atomic.AddInt32--增减
    • 5.2 CAS-atomic.CompareAndSwapInt32--比较并替换
    • 5.3 atomic.StoreInt32--写操作
    • 5.4 atomic.LoadInt32--读操作
    • 5.5 atomic.SwapInt32--直接交换

1.go已经提供了锁,为什么还需要atomic原子操作?

1.加锁代价比较高,耗时多,需要上下文切换。加锁解锁在代码层实现,而代码是运行在用户态空间中,对底层进行操作时需要从用户态空间切换到内核空间,再由内核操作底层资源。耗时多

2.原子操作在用户态可以完成,性能比互斥锁高。原子操作在cpu层面支持的,cpu可以直接操作底层资源

3.原子操作需求步骤简单,无需加锁解锁步骤

2.atomic原子操作为什么比mutex快?

1.原子操作快,是因为依赖于cpu指令,而不是依赖外部锁。不会额外的上下文切换
2.原子操作能够保证执行期间是连续且不会被中断(变量不会被其他修改,mutex可能存在被其他修改的情况)

3.CAS

CAS是cpu硬件同步原语,是Compare And Swap的缩写(比较并交换),原子操作中CAS,再sync/atomic包中,全部以ComparAndSwap开头的函数名都是CAS操作
   go中CAS操作,是借用CPU提供的原子性指令来实现。CAS操作修改共享变量时,不需要对共享变量加锁,而是通过类似乐观锁的方式进行检查,本质还是不断的占用CPU资源换取加锁带来的开销(如上下文切换时间开销)。

原子操作优势:
   可以在不形成临界区和创建互斥量的情况下完成并发安全的值替换操作。这可以大大的减少同步对程序性能的损耗。

原子操作劣势:
   在被操作值被频繁的变更的情况下,CAS操作并不那么容易成功。因为需要对ild值进行匹配,只有匹配成功了才进行下一步的修改。

当前atmomic包有以下几种原子操作:
   Add,ComparAndSwap,Load,Store,Swap

4.互斥锁与原子操作区别

互斥锁目的:互斥锁是用来保护一段逻辑的,保证并发安全。(比如操作数据库保护)
原子操作目的:原子操作作用于一个变量的更新保护,保证并发安全(比如操作数据库不能原子操作)

mutex底层实现:mutex由操作系统的调度器实现
原子操作底层实现:由底层硬件指令直接提供支持,这些指令在执行过程中不允许中断,因此原子操作可以在无锁的情况下保证并发安全,性能随cpu的数量增多而线性扩展。

5.原子操作方法

5.1 atomic.AddInt32--增减

增减,操作方法的命名方式为AddXXX,保证对操作数进行原子的增减,支持的类型为int32、int64、uint32、uint64、uintptr,使用时以AddXXX就是对应的操作方法。

//加
func demo() {
	var count int32 = 0
	atomic.AddInt32(&count, 10)
	fmt.Println(count) //10
}
//减
func demo() {
	var count int32 = 0
	atomic.AddInt32(&count, -10)
	fmt.Println(count) //-10
}

锁和原子操作对比:

//Mutex锁
func demo1() {
	sta := time.Now().Unix()
	count := 0
	mux := sync.Mutex{}
	wg := sync.WaitGroup{}
	for i := 0; i < 10000; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for j := 0; j < 10000; j++ {
				mux.Lock()
				count++
				mux.Unlock()
			}
		}()
	}
	wg.Wait()
	fmt.Println(count) //100000000
	fmt.Println(time.Now().Unix() - sta) //10秒
}

//atomic原子操作:快2倍不止
func demo2() {
	sta := time.Now().Unix()
	wg := sync.WaitGroup{}
	var count int32 = 0
	for i := 0; i < 10000; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			for j := 0; j < 10000; j++ {
				atomic.AddInt32(&count, 1)
			}
		}()
	}
	wg.Wait()
	fmt.Println(count) //100000000
	fmt.Println(time.Now().Unix() - sta) //4秒
}

5.2 CAS-atomic.CompareAndSwapInt32--比较并替换

CompareAndSwap:比较并替换,类似乐观锁,先比较下old值与当前值是否一致,一致则把new的值替换
操作方法的命名方式为CompareAndSwapXXX

//true
func demo3() {
	var count int32 = 0
	boo := atomic.CompareAndSwapInt32(&count, 0, 100)
	fmt.Println(count) //100
	fmt.Println(boo)   //true
}

//false
func demo3() {
	var count int32 = 0
	boo := atomic.CompareAndSwapInt32(&count, 10, 100)
	fmt.Println(count) //0
	fmt.Println(boo) //false
}

5.3 atomic.StoreInt32--写操作

func demo3() {
	var count int32 = 0
	atomic.StoreInt32(&count, 666)
	fmt.Println(count) //666
}

5.4 atomic.LoadInt32--读操作

func demo3() {
	var count int32 = 0
	atomic.StoreInt32(&count, 666)

	val := atomic.LoadInt32(&count)
	fmt.Println(val) //666
}

5.5 atomic.SwapInt32--直接交换

atomic.SwapInt32:直接交换,并返回交换前的值

func demo3() {
	var count int32 = 0
	old := atomic.SwapInt32(&count, 100)
	fmt.Println(old)   //0
	fmt.Println(count) //100
}

到此这篇关于goland-sync/atomic原子操作的文章就介绍到这了,更多相关goland sync/atomic原子操作内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • goland 清除所有的默认设置操作

    前不久用goland时,不小心将.tpl文件默认为.go文件.然后就开始报错,setting找了很久,由于英语比较差,翻译了很久还是没有找到恢复默认设置的选项. 最后找到了另一种解决方法.如果有知道怎么在setting中清除设置的朋友,麻烦回复一下啦. 在我首次创建.tpl文件时出现以下选项,因为系统并不知道.tpl文件是什么,所以首次创建需要你选择文件类型. limo当时脑子一热就选了.go文件,所以开始报错 我的解决方法是找到如下路径(系统是Window7) 删除文件(清除了所有的设置) 重

  • goland设置颜色和字体的操作

    如下所示: 补充:Goland调整Terminal窗口字体大小 Goland的Ternimal窗口样式和Console窗口公用同一个样式,修改路径: Setting->Editor->Color Scheme->Console Font 若不生效,重启一下IDE即可. 以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们.如有错误或未考虑完全的地方,望不吝赐教.

  • Goland 断点调试Debug的操作

    第一步:进入编辑模式 第二步:开始进行编辑 第三步:就可以进行调式了 补充:goland断点调试报错 一.运行报错 runnerw.exe: CreateProcess failed with error 216 \(no message available\) 解决办法: 查看package是不是main,同时必须有个main函数,作为程序运行的主入口 查看GOOS是不是本系统的,比如windows,得设置为同一个,cmd中使用set GOOS=windows,goland中设置方法如下: 二

  • goland -sync/atomic原子操作小结

    目录 1.go已经提供了锁,为什么还需要atomic原子操作? 2.atomic原子操作为什么比mutex快? 3.CAS 4.互斥锁与原子操作区别 5.原子操作方法 5.1 atomic.AddInt32--增减 5.2 CAS-atomic.CompareAndSwapInt32--比较并替换 5.3 atomic.StoreInt32--写操作 5.4 atomic.LoadInt32--读操作 5.5 atomic.SwapInt32--直接交换 1.go已经提供了锁,为什么还需要ato

  • CountDownLatch和Atomic原子操作类源码解析

    目录 引导语 1.CountDownLatch 1.1.await 1.2.countDown 1.3.示例 2.Atomic原子操作类 3.总结 引导语 本小节和大家一起来看看 CountDownLatch 和 Atomic 打头的原子操作类,CountDownLatch 的源码非常少,看起来比较简单,但 CountDownLatch 的实际应用却不是很容易:Atomic 原子操作类就比较好理解和应用,接下来我们分别来看一下. 1.CountDownLatch CountDownLatch 中

  • C++11并发编程关于原子操作atomic的代码示例

    一:概述 项目中经常用遇到多线程操作共享数据问题,常用的处理方式是对共享数据进行加锁,如果多线程操作共享变量也同样采用这种方式. 为什么要对共享变量加锁或使用原子操作?如两个线程操作同一变量过程中,一个线程执行过程中可能被内核临时挂起,这就是线程切换,当内核再次切换到该线程时,之前的数据可能已被修改,不能保证原子操作. C++11提供了个原子的类和方法atomic,保证了多线程对变量原子性操作,相比加锁机制mutex.lock(),mutex.unlock(),性能有几倍的提升. 所需头文件<a

  • Go语言并发之原子操作详解

    目录 修改 赋值与读取 比较并交换 小结 代码中的加锁操作因为涉及内核态的上下文切换会比较耗时.代价比较高.针对基本数据类型我们还可以使用原子操作来保证并发安全,因为原子操作是Go语言提供的方法它在用户态就可以完成,因此性能比加锁操作更好.Go语言中原子操作由内置的标准库sync/atomic 提供. 大多数情况下我们都是针对基本数据类型进行数据操作,能不加锁就不加锁. 首先很多人都不相信基本类型并发修改会出现竞态问题.不妨尝试一下,并发加一. var wg sync.WaitGroup for

  • Go 并发读写 sync.map 详细

    目录 1.sync.Map 优势 2.性能测试 2.1 压测结果 1)写入 2)查找 3)删除 2.3 场景分析 3.sync.Map 剖析 3.1 数据结构 3.2 查找过程 3.3 写入过程 3.4 删除过程 map 的两种目前在业界使用的最多的并发支持的模式分别是: 原生 map + 互斥锁或读写锁 mutex. 标准库 sync.Map(Go1.9及以后). 有了选择,总是有选择困难症的,这两种到底怎么选,谁的性能更加的好?我有一个朋友说 标准库 sync.Map 性能菜的很,不要用.我

  • Go并发编程之sync.Once使用实例详解

    目录 一.序 二. 源码分析 2.1结构体 2.2 接口 三. 使用场景案例 3.1 单例模式 3.2 加载配置文件示例 四.总结 五. 参考 一.序 单从库名大概就能猜出其作用.sync.Once使用起来很简单, 下面是一个简单的使用案例 package main import ( "fmt" "sync" ) func main() { var ( once sync.Once wg sync.WaitGroup ) for i := 0; i < 10;

  • Go语言原子操作及互斥锁的区别

    目录 增或减 比较并交换(Compare And Swap) 载入与存储 交换 原子值 原子操作与互斥锁的区别 原子操作就是不可中断的操作,外界是看不到原子操作的中间状态,要么看到原子操作已经完成,要么看到原子操作已经结束.在某个值的原子操作执行的过程中,CPU绝对不会再去执行其他针对该值的操作,那么其他操作也是原子操作. Go语言中提供的原子操作都是非侵入式的,在标准库代码包sync/atomic中提供了相关的原子函数. 增或减 用于增或减的原子操作的函数名称都是以"Add"开头的,

  • Golang的锁机制与使用技巧小结

    目录 1. sync.Mutex详解 2. RWMutex详解 3. sync.Map详解 4. 原子操作 atomic.Value 5. 使用小技巧 1. sync.Mutex详解 sync.Mutex是Go中的互斥锁,通过.lock()方法上锁,.unlock()方法解锁.需要注意的是,因为Go函数值传递的特点,sync.Mutex通过函数传递时,会进行一次拷贝,所以传递过去的锁是一把全新的锁,大家在使用时要注意这一点,另外sync.Mutex是非重入锁,这一点要与Java中的锁区分. ty

  • GoLang的sync.WaitGroup与sync.Once简单使用讲解

    目录 一.sync.WaitGroup的简单实用 Add方法 Done方法 Wait方法 二.sync.WaitGroup类型值中计数器的值可以小于0吗 三.sync.Once 用法 sync.Once类型中的uint32类型的字段 Do方法的功能特点 一.sync.WaitGroup的简单实用 在之前,我们使用通道,来主goroutine中等待其他goroutine执行完成: func coordinateWithChan() { sign := make(chan struct{}, 2)

  • GO语言中通道和sync包的使用教程分享

    目录 GO通道和 sync 包的分享 通道是什么 通道能做什么 通道有哪几种 无缓冲通道 有缓冲的通道 单向通道 如何创建和声明一个通道 声明通道 初始化通道 如何操作 channel 通道异常情况梳理 每一种通道的DEMO实战 无缓冲通道 有缓冲通道 单向通道 关闭通道 总结 GO通道和 sync 包的分享 我们一起回顾一下上次分享的内容: GO协程同步若不做限制的话,会产生数据竞态的问题 我们用锁的方式来解决如上问题,根据使用场景选择使用互斥锁 和 读写锁 比使用锁更好的方式是原子操作,但是

随机推荐