美文网首页
Go 共享内存并发:从入门到精通,这一篇就够了!

Go 共享内存并发:从入门到精通,这一篇就够了!

作者: 可乐他爸 | 来源:发表于2025-02-03 23:11 被阅读0次

什么是共享内存并发?

在传统的并发模型中,多个线程或 Goroutine 共享同一块内存空间。这意味着它们可以直接访问和修改相同的数据,而无需进行显式的数据传递。这种方式可以提高效率,但也带来了数据竞争和同步问题。

Go 语言中的共享内存并发

在 Go 语言中,Goroutine 是轻量级的并发执行单元。多个 Goroutine 可以并发地访问和修改共享内存。

示例代码:

package main

import (
    "fmt"
    "sync"
)

var counter int = 0
var wg sync.WaitGroup //`sync.WaitGroup`是一个用于等待一组goroutine完成的同步机制

func increment() {
    defer wg.Done() //在函数返回前调用Done()
    for i := 0; i < 5000; i++ {
        counter++ // 潜在的数据竞争
    }
}

func main() {
    wg.Add(2) //告诉`WaitGroup`你将要启动2个goroutine
    go increment()
    go increment()
    wg.Wait() //在所有goroutine启动后,使用`Wait()`方法阻塞当前goroutine,直到所有通过`Add()`添加的任务都通过调用`Done()`完成
    fmt.Println("Counter:", counter)
}

问题:数据竞争

上面的代码看似简单,但存在严重的数据竞争问题。由于多个 Goroutine 同时修改 counter 变量,导致最终结果可能不正确。

如何解决数据竞争?

Go 语言提供了多种机制来解决共享内存并发中的数据竞争问题:

  • 互斥锁(Mutex): 互斥锁是最常用的同步机制。它可以确保在同一时刻只有一个 Goroutine 可以访问共享资源。

    package main
    
    import (
        "fmt"
        "runtime"
        "sync"
    )
    
    var counter int = 0
    var wg sync.WaitGroup 
    var mu sync.Mutex // 互斥锁
    
    func increment() {
        defer wg.Done()  
        for i := 0; i < 1000; i++ {
            mu.Lock()   // 加锁
            counter++
            mu.Unlock() // 解锁
        }
    }
    
    func main() {
        wg.Add(2) 
        go increment()
        go increment()
        wg.Wait()
        fmt.Println("Counter:", counter)
    }
    
  • 读写锁(RWMutex): 读写锁允许多个 Goroutine 同时读取共享资源,但只允许一个 Goroutine 写入共享资源。这在读多写少的场景下可以提高性能。

    package main
    
    import (
        "fmt"
        "runtime"
        "sync"
    )
    
    var counter int = 0
    var wg sync.WaitGroup
    var rwmu sync.RWMutex // 读写锁
    
    func readCounter() {
        defer wg.Done()
        rwmu.RLock()   // 读锁
        fmt.Println("Counter:", counter)
        rwmu.RUnlock() // 释放读锁
    }
    
    func increment() {
        defer wg.Done()
        rwmu.Lock()   // 写锁
        counter++
        rwmu.Unlock() // 释放写锁
    }
    
    func main() {
        wg.Add(3)
        go readCounter()
        go increment()
        go readCounter()
        wg.Wait()
        fmt.Println("Final Counter:", counter)
    }
    
  • 原子操作(Atomic Operations): 原子操作是不可分割的操作,可以保证在多线程环境下对共享变量的访问是安全的。

    package main
    
    import (
        "fmt"
        "runtime"
        "sync"
        "sync/atomic"
    )
    
    var counter int64 = 0
    var wg sync.WaitGroup
    
    func increment() {
        defer wg.Done()
        for i := 0; i < 5000; i++ {
            atomic.AddInt64(&counter, 1) // 原子操作
        }
    }
    
    func main() {
        wg.Add(2)
        go increment()
        go increment()
        wg.Wait()
        fmt.Println("Counter:", counter)
    }
    

总结

共享内存并发是 Go 语言中实现高性能并发应用的重要技术。通过理解其原理、掌握其用法,并避免常见的陷阱,你可以编写出高效、可靠的并发程序。

感谢阅读!如果你觉得这篇文章对你有帮助,请分享给你的朋友们,让更多的人一起学习Go语言!

相关文章

  • android内存泄露

    参考内存泄露从入门到精通三部曲之基础知识篇Android 内存泄漏总结Android内存泄漏研究Android内存...

  • golang并发总结

    golang并发模型 go在语言层面提供了内置的并发支持 不要通过共享内存来通信,而应该通过通信来共享内存 并发与...

  • go并发通信

    go并发编程时,请记住:“不要通过共享内存来通信,而应该通过通信来共享内存” channel是Go语言在语言级别提...

  • RabbitMQ 入门到精通(这一篇就够了)

    一、 什么是 RabbitMQ RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消...

  • 想要安全地保管资产,先要知道钱包的这些知识

    原创: 白话编辑部 白话区块链 2018-12-10 白话区块链 从入门到精通,看我就够了! 「白话区块链入门系列...

  • 2022-05-20

    想学习Go语言 找了资料 尚硅谷_韩顺平GO语言 从入门到精通 https://www.bilibili.com/...

  • Redis从入门到精通:初级篇

    Redis从入门到精通:初级篇 Redis从入门到精通:初级篇 平时陆陆续续看了不少Redis的文章了,工作中也一...

  • channel的使用及源码解析

    简介 熟悉Go的人都知道,它提倡着不要通过共享内存来通讯,而要通过通讯来共享内存。Go提供了一种独特的并发同步技术...

  • netty通信框架

    Netty入门教程——认识Netty彻底理解Netty,这一篇文章就够了 Netty特点 并发高 传输快 封装好 ...

  • 白话区块链入门005 | 比特币是什么?比特币和Q币有什么不同?

    白话区块链:从入门到精通,看我就够了! 「白话区块链入门系列」是白话区块链针对新入门用户、推出的一档区块链科普栏目...

网友评论

      本文标题:Go 共享内存并发:从入门到精通,这一篇就够了!

      本文链接:https://www.haomeiwen.com/subject/dpwbujtx.html