4 min read

Go 并发编程

Go 并发编程写法

Table of Contents

You:

go 并发编程写法

ChatGPT:

Go(Golang)是为并发编程而生的语言,内建对并发的原生支持,主要通过以下几个核心机制来实现:

  • goroutine(轻量线程)

  • channel(通信机制)

  • select(监听多个channel)

  • sync 包(同步原语,如 Mutex、WaitGroup 等)

下面通过例子逐一讲解这些核心机制的写法。


1. 启动 goroutine

package main

import (
    "fmt"
    "time"
)

func say(msg string) {
    for i := 0; i < 3; i++ {
        fmt.Println(msg)
        time.Sleep(time.Millisecond * 500)
    }
}

func main() {
    go say("Hello")     // 开一个 goroutine
    say("World")        // 主线程执行
}

输出结果是交错的,因为 say("Hello") 是在一个 goroutine 中异步执行的。


2. 使用 channel 通信

package main

import "fmt"

func sum(a []int, c chan int) {
    total := 0
    for _, v := range a {
        total += v
    }
    c <- total // 将结果发送到 channel
}

func main() {
    a := []int{1, 2, 3, 4, 5}
    c := make(chan int)

    go sum(a[:len(a)/2], c)
    go sum(a[len(a)/2:], c)

    x, y := <-c, <-c // 接收两个结果
    fmt.Println(x, y, x+y)
}

  1. 使用 select 多路复用
package main

import (
    "fmt"
    "time"
)

func main() {
    c1 := make(chan string)
    c2 := make(chan string)

    go func() {
        time.Sleep(1 * time.Second)
        c1 <- "from c1"
    }()
    go func() {
        time.Sleep(2 * time.Second)
        c2 <- "from c2"
    }()

    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-c1:
            fmt.Println("Received:", msg1)
        case msg2 := <-c2:
            fmt.Println("Received:", msg2)
        }
    }
}

4. 使用 WaitGroup 同步

package main

import (
    "fmt"
    "sync"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done() // 每完成一个任务就减1
    fmt.Printf("Worker %d starting\n", id)
    // 模拟工作
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    var wg sync.WaitGroup

    for i := 1; i <= 3; i++ {
        wg.Add(1)
        go worker(i, &wg)
    }

    wg.Wait() // 阻塞直到所有 goroutine 完成
    fmt.Println("All workers done")
}

5. 使用 Mutex 保护共享资源

package main

import (
    "fmt"
    "sync"
)

type Counter struct {
    mu sync.Mutex
    val int
}

func (c *Counter) Inc() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.val++
}

func (c *Counter) Value() int {
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.val
}

func main() {
    c := &Counter{}
    var wg sync.WaitGroup

    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            c.Inc()
        }()
    }

    wg.Wait()
    fmt.Println("Final Counter:", c.Value())
}

总结:并发写法关键点

功能写法简述
启动协程go func()
创建通道ch := make(chan int)
通信ch <- x(发送),x := <-ch(接收)
等待多个协程sync.WaitGroup
数据保护sync.Mutex
多通道选择select { case ... }

You:

func hello() {
fmt.Println("Hello Goroutine!")
}
func main() {
go hello()
fmt.Println("main goroutine done!")
time.Sleep(time.Second)
}

假如我不想用time.Sleep来保证hello被执行,还有什么别的办法

ChatGPT:

是的,不推荐使用 time.Sleep 来等待 goroutine 完成,这种方式既不可靠也不优雅。更合理的方式是使用 sync.WaitGroup** ,它专门用来等待一组 goroutine 完成。


✅ 使用 sync.WaitGroup 的写法如下:

package main

import (
	"fmt"
	"sync"
)

func hello(wg *sync.WaitGroup) {
	defer wg.Done() // goroutine 完成时调用 Done
	fmt.Println("Hello Goroutine!")
}

func main() {
	var wg sync.WaitGroup
	wg.Add(1) // 表示有一个 goroutine 要等待

	go hello(&wg)

	fmt.Println("main goroutine waiting...")
	wg.Wait() // 等待所有 goroutine 结束
	fmt.Println("main goroutine done!")
}

Jason Lee
Hi, I'm Jason Lee

I hope I can still be curious about the world when I turn 60 years old.

Enjoy!

Contact me:

Email | GitHub


Content licenced under CC BY-NC-ND 4.0