在Go语言中,实现并发访问内存主要依赖于goroutine和channel。Go的并发模型是基于CSP(Communicating Sequential Processes)理论的,通过goroutine来执行并发任务,并通过channel来进行goroutine之间的通信。以下是一些关键的概念和方法:

1. 创建和启动goroutine

func main() {
    go someFunction()
}

func someFunction() {
    // 并发执行的代码
}

在上面的代码中,go someFunction()创建并启动了一个新的goroutine,它将与主goroutine并发执行。

2. 使用channel进行通信

func main() {
    done := make(chan bool) // 创建一个channel

    go func() {
        // 做一些工作
        done <- true // 通过channel发送一个信号
    }()

    <-done // 从channel接收信号,直到goroutine完成工作
}

在这个例子中,我们创建了一个channel done,然后在一个goroutine中向它发送一个值。主goroutine通过从done channel接收值来等待goroutine完成。

3. 并发执行多个任务

func someFunction() {
    // 任务代码
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1) // 增加等待组的计数
        go func() {
            someFunction()
            wg.Done() // 任务完成后减少计数
        }()
    }
    wg.Wait() // 等待所有goroutine完成
}

在这个例子中,我们使用了sync.WaitGroup来等待多个goroutine完成。每个goroutine在开始前调用wg.Add(1),在完成后调用wg.Done()

4. 并发访问共享资源

var mu sync.Mutex // 创建一个互斥锁
var counter int

func increment() {
    mu.Lock()         // 锁定互斥锁
    counter++         // 并发安全地增加计数
    mu.Unlock()       // 解锁
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
            increment()
            wg.Done()
        }()
    }
    wg.Wait()
    println(counter) // 应该输出1000
}

在这个例子中,我们使用sync.Mutex来保护对共享资源counter的并发访问。在修改counter之前,我们通过mu.Lock()获取锁,并在修改后通过mu.Unlock()释放锁。

5. 使用原子操作

对于一些简单的操作,可以使用sync/atomic包中的原子操作来避免使用互斥锁。

package main

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

func main() {
    counter := int32(0)
    const workers = 100
    const operations = 1000

    var wg sync.WaitGroup
    wg.Add(workers)

    for i := 0; i < workers; i++ {
        go func() {
            for j := 0; j < operations; j++ {
                atomic.AddInt32(&counter, 1)
            }
            wg.Done()
        }()
    }

    wg.Wait()
    fmt.Println(counter) // 应该输出 operations * workers
}

在这个例子中,我们使用atomic.AddInt32函数来原子地增加counter的值,这样可以避免在多个goroutine中对同一变量进行操作时发生竞态条件。

通过上述方法,Go语言提供了强大的并发编程能力,允许开发者以安全和高效的方式并发访问内存。

04-07 06:05