GoLang 语言高级(1)

核心概念速览:

  • goroutine → 轻量级并发执行单元
  • channel → goroutine 之间传递数据
  • select → 监听多个 channel
  • WaitGroup → 等待一组 goroutine 完成
  • Mutex → 保护共享数据

goroutine 轻量级并发执行单元

goroutine 是 GoLang 提供的一种用户级线程(协程)。Go Runtime 将用户编写的大量 goroutine 调度到少量的操作系统级别的线程上执行。

go 复制代码
goroutine (G) -> Go Runtime 调度 -> OS Thread(线程) -> CPU

这就是 Go 最著名的 GMP 模型。

go 复制代码
package main

import "fmt"

func test() {
    fmt.Println("hello")
}

func main() {
    // 普通的函数调用前加上 go 关键字,该函数就会被当作一个 goroutine 执行
    go test()
}

不过以上的这个模型可能不会有任何输出。因为 main 函数退出,所有的 goroutine 都得退出,不管你执行到了哪里。

可以通过 time.Sleep() 方法,阻塞主进程,让 goroutine 能够有足够的时间执行完毕。

go 复制代码
package main

import (
    "fmt"
    "time"
)

func hello() {
    fmt.Println("hello")
}

func main() {
    go hello()

    time.Sleep(time.Second)
}

WaitGroup

在上面使用了 time.Sleep() 方法来等待 goroutine 执行完毕,仅做演示用。在正式项目中正规的做法是使用 WaitGroup 机制等待 goroutine 执行完毕。

go 复制代码
package main

import (
    "fmt"
    "sync"
)

func hello(wg *sync.WaitGroup) {
    defer wg.Done()

    fmt.Println("hello")
}

func main() {
    var wg sync.WaitGroup

    wg.add(1)
    go hello(&wg)

    // 等待 hello 执行完毕
    wg.wait()
}

启动多个 goroutine 并等待它们全部执行完毕。

go 复制代码
package main

import (
    "fmt"
    "sync"
)

func worker(i int, wg *sync.WaitGroup) {
    defer wg.Done()

    fmt.Println("worker: " + i)
}

func main() {
    var wg sync.WaitGroup

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

通过匿名函数传递参数,容易出现闭包陷阱的问题。

(闭包捕获循环变量,很多编程语言都有的 bug, Go 1.22 修复了这个问题)

go 复制代码
package main

import "fmt"

func main() {

    // 正确方式
    for i := 0; i < 5; i++ {
        go func(i int) {
            fmt.Println(i)
        }(i)
    }

    // 不推荐的方式
    for j := 0; j < 5; j++ {
        go func() {
            fmt.Println(j)
        }()
    }
}

channel

channel 是一个带有同步能力的队列。

go 复制代码
goroutine A -> channel -> goroutine B

例如:

go 复制代码
package main

func main() {

    ch := make(chan int)

    go func() {
        ch <- 42
    }()

    fmt.Println(<-ch)
}

这个例子体现了 channel 天生就是自带同步功能的。这其中发生了两件事情,数据传递和同步。

数据传递:

go 复制代码
42 -> channel -> main

同步:

go 复制代码
<-ch 没有数据时,会阻塞 main
直到 goroutine ch <- 42,main 接收到了 goroutine 发送过来的 数据,才继续向下执行

带缓冲 channel

通过以下语法:

go 复制代码
ch := make(chan int, 3)

可以创建一个带缓冲的 channel

发送方可以连续发送:

go 复制代码
ch <- 1
ch <- 2
ch <- 3

不会阻塞。

发送了第四个

go 复制代码
ch <- 4 时,发送方才会阻塞

所以带缓冲的 channel 可以当作异步通信,而没带缓冲的 channel 可以被当作同步通信。

这是这两类 channel 之间的区别。

相关推荐
葫芦和十三18 小时前
图解 MongoDB 24|分片为什么存在:垂直扩容的天花板
后端·mongodb·agent
有趣的老凌18 小时前
用 Vibe Coding 搭了一个完整小程序「一定能成」
前端·javascript·后端
葫芦和十三1 天前
图解 MongoDB 23|两地三中心:跨可用区部署怎么扛机房故障
后端·mongodb·agent
勇哥java实战分享1 天前
PaddleOCR 太慢?我换成 RapidOCR 后,速度直接起飞
后端
苏三说技术1 天前
LangChain4j 和 LangGraph4j,哪个更好?
后端
ServBay1 天前
7 个AI开发中真正用得上的 MCP Server,配合Claude Code食用效果更佳
后端·claude·mcp
妙码生花1 天前
从 PHP 到 AI + Golang,程序员自救转型手记(十五):优化细节、网络请求封装
前端·后端·ai编程
用户6757049885021 天前
Go 语言里判断字符串为空,90% 的人都写错了!
后端·go
用户6757049885021 天前
Go 进阶必修:90% 的人都没用对的“表驱动法”
后端·go