jee said

Go语言并发编程详解

unclejee & AI
644 words
4min read
Go语言并发编程详解

Go语言并发编程详解

Go语言的并发模型是其最强大的特性之一。通过goroutine和channel,Go让并发编程变得简单而高效。

Goroutine

Goroutine是Go语言的轻量级线程,由Go运行时管理。

启动Goroutine

func sayHello() {
    fmt.Println("Hello, Goroutine!")
}

func main() {
    go sayHello()  // 启动goroutine
    time.Sleep(time.Second)  // 等待goroutine执行
}

Goroutine特性

  • 轻量级:初始栈大小仅2KB
  • 动态扩展:栈空间按需增长
  • 高效调度:M:N调度模型
  • 简单易用:只需添加go关键字

Channel

Channel是goroutine之间通信的主要方式,遵循”不要通过共享内存来通信,而要通过通信来共享内存”的理念。

创建和使用Channel

// 创建channel
ch := make(chan int)

// 发送数据
ch <- 42

// 接收数据
value := <-ch

缓冲Channel

// 创建缓冲大小为3的channel
ch := make(chan int, 3)

ch <- 1  // 不会阻塞
ch <- 2
ch <- 3

Select语句

select {
case msg1 := <-ch1:
    fmt.Println("从ch1接收:", msg1)
case msg2 := <-ch2:
    fmt.Println("从ch2接收:", msg2)
case <-time.After(time.Second):
    fmt.Println("超时")
}

Sync包

当需要更细粒度的同步控制时,可以使用sync包。

WaitGroup

var wg sync.WaitGroup

func worker(id int) {
    defer wg.Done()
    fmt.Printf("Worker %d 完成\n", id)
}

func main() {
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go worker(i)
    }
    wg.Wait()  // 等待所有goroutine完成
}

Mutex

var (
    counter int
    mu      sync.Mutex
)

func increment() {
    mu.Lock()
    defer mu.Unlock()
    counter++
}

Once

var once sync.Once

func setup() {
    once.Do(func() {
        fmt.Println("只执行一次")
    })
}

Context

Context用于控制goroutine的生命周期和取消操作。

func worker(ctx context.Context, id int) {
    for {
        select {
        case <-ctx.Done():
            fmt.Printf("Worker %d 被取消\n", id)
            return
        default:
            // 执行工作
        }
    }
}

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel()
    go worker(ctx, 1)
    time.Sleep(3 * time.Second)
}

最佳实践

  1. 优先使用channel:channel是Go并发编程的首选方式
  2. 避免共享状态:减少锁的使用,提高性能
  3. 合理使用缓冲:根据场景选择缓冲或非缓冲channel
  4. 及时关闭channel:使用defer确保资源释放
  5. 使用context控制生命周期:优雅地取消goroutine

总结

Go语言的并发模型简洁而强大,通过goroutine和channel可以轻松实现高效的并发程序。掌握这些概念后,你就能充分利用Go语言的并发优势了。