【Go语言学习系列33】并发编程(六):原子操作与内存模型

📚 原创系列: “Go语言学习系列”

🔄 转载说明: 本文最初发布于"Gopher部落"微信公众号,经原作者授权转载。

🔗 关注原创: 欢迎扫描文末二维码,关注"Gopher部落"微信公众号获取第一手Go技术文章。

📑 Go语言学习系列导航

本文是【Go语言学习系列】的第33篇,当前位于第三阶段(进阶篇)

🚀 第三阶段:进阶篇
  1. 并发编程(一):goroutine基础
  2. 并发编程(二):channel基础
  3. 并发编程(三):select语句
  4. 并发编程(四):sync包
  5. 并发编程(五):并发模式
  6. 并发编程(六):原子操作与内存模型 👈 当前位置
  7. 数据库编程(一):SQL接口
  8. 数据库编程(二):ORM技术
  9. Web开发(一):路由与中间件
  10. Web开发(二):模板与静态资源
  11. Web开发(三):API开发
  12. Web开发(四):认证与授权
  13. Web开发(五):WebSocket
  14. 微服务(一):基础概念
  15. 微服务(二):gRPC入门
  16. 日志与监控
  17. 第三阶段项目实战:微服务聊天应用

📚 查看完整Go语言学习系列导航

📖 文章导读

在本文中,您将了解:

  • 什么是原子操作,为什么它在并发编程中如此重要
  • sync/atomic包提供的基本操作和新的原子类型
  • Go内存模型中的happens-before关系及其重要性
  • 内存重排和其对并发程序的影响
  • 如何避免常见的并发陷阱和优化并发性能
  • 使用原子操作实现高效的并发数据结构

Go原子操作与内存模型

并发编程(六):原子操作与内存模型

1. 什么是原子操作

在并发编程中,**原子操作(Atomic Operations)**是指不会被线程调度机制中断的操作,这类操作一旦开始,就会在CPU的一个时钟周期内执行完毕。原子操作在执行过程中不会被其他线程或进程打断,因此可以保证操作的完整性和一致性。

原子操作的主要特点:

  1. 不可分割性:原子操作是最小的执行单位,它要么完全执行,要么完全不执行
  2. 并发安全:多个线程同时对同一变量进行原子操作不会导致数据竞争
  3. 可见性保证:原子操作还确保操作的结果对其他线程可见

在Go语言中,原子操作是通过sync/atomic包实现的,它提供了低级别的原子内存原语,用于实现同步算法。

2. 为什么需要原子操作

我们知道在并发编程中可以使用互斥锁(Mutex)来保护共享资源,那为什么还需要原子操作呢?以下是几个主要原因:

2.1 性能考虑

相比互斥锁,原子操作通常有更好的性能,因为它们:

  • 不需要操作系统的调度器参与
  • 直接在硬件层面实现
  • 避免了上下文切换的开销

请看这个简单的性能对比:

package main

import (
    "sync"
    "sync/atomic"
    "testing"
)

func BenchmarkMutex(b *testing.B) {
   
   
    var count int64
    var mu sync.Mutex
    
    for i := 0; i < b.N; i++ {
   
   
        mu.Lock()
        count++
        mu.Unlock()
    }
}

func BenchmarkAtomic(b *testing.B) {
   
   
    var count int64
    
    for i := 0; i < b.N; i++ {
   
   
        atomic.AddInt64(&count, 1)
    }
}

在绝大多数情况下,原子操作的性能会比互斥锁高出数倍甚至数十倍。

2.2 适用于简单操作

原子操作非常适合于对单个变量进行简单操作,如:

  • 递增/递减计数器
  • 交换值
  • 比较并交换(CAS)操作

2.3 避免死锁风险

使用原子操作可以避免某些死锁场景,因为它们不会阻塞线程。

3. sync/atomic包的基本操作

Go语言的sync/atomic包提供了几类基本操作:

3.1 加载和存储操作

// 读取操作 - 原子地加载值
func Load(addr *T) T
// 存储操作 - 原子地存储值
func Store(addr *T, val T)

这些函数确保在多个goroutine之间读写变量时不会导致数据竞争。

示例:原子加载和存储
package main

import (
    "fmt"
    "sync/atomic"
    "time"
)

func main() {
   
   
    var value int32 = 0
    
    // 并发写入
    go func() {
   
   
        for i := 0; i < 5; i++ {
   
   
            atomic.StoreInt32(&value, int32(i))
            time.Sleep(100 * time.Millisecond)
        }
    }()
    
    // 并发读取
    go func() {
   
   
        for i := 0; i < 10; i++ {
   
   
            val := atomic.LoadInt32(&value)
            fmt.Println("Value:", val)
            time.Sleep(50 * time.Millisecond)
        }
    }()
    
    time.Sleep(1 * time.Second)
}

3.2 增减操作

// 增加操作 - 原子地将delta加到addr并返回新值
func Add(addr *T, delta T) T
// 减一操作 - 原子地将addr减1并返回新值
func AddInt64(addr *int64, delta int64) (new int64)
示例:原子计数器
package main

import (
    "fmt"
    "sync"
    "sync/atomic"
)

func main() {
   
   
    var counter int64 = 0
    var wg sync.WaitGroup
    
    // 启动100个goroutine,每个对计数器增加100
    for i := 0; i < 100; i++ {
   
   
        wg.Add(1)
        go func() {
   
   
            defer wg.Done()
            for j := 0; j < 100; j++ {
   
   
                atomic.AddInt64(&counter, 1)
            }
        }()
    }
    
    wg.Wait()
    fmt.Println("Final Counter:", counter) // 期望输出: 10000
}

3.3 交换操作

// 原子地将新值存入addr并返回旧值
func Swap(addr *T, new T) (old T)
示例:原子交换值
package main

import (
    "fmt"
    "sync/atomic"
)

func main() {
   
   
    var value int32 = 100
    
    // 原子地将值从100交换成200,并获取旧值
    oldValue := atomic.SwapInt32(&value, 200)
    
    fmt.Println("Old value:", oldValue) // 输出: 100
    fmt.Println("New value:", value)    // 输出: 200
}

3.4 比较并交换(CAS)操作

// 如果addr的值等于old,则将new写入addr
func CompareAndSwap(addr *T, old, new T) (swapped bool)

CAS操作是实现无锁数据结构的基础,它允许我们在不使用互斥锁的情况下安全地修改值。

示例:使用CAS实现自旋锁
package main

import (
    "fmt"
    "sync"
    "sync/atomic"
)

// 简单的自旋锁实现
type SpinLock struct {
   
   
    locked int32
}

// 尝试获取锁
func (l *SpinLock) Lock() {
   
   
    // 不断尝试将locked从0设为1
    for !atomic.CompareAndSwapInt32(&l.locked, 0, 1) {
   
   
        // 自旋等待
    }
}

// 释放锁
func (l *SpinLock
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gopher部落

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值