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 之间的区别。

相关推荐
迅猛龙办公室1 分钟前
Python函数实现反转一个只有3位数的整数
开发语言·前端·python
“AI国潮设计-小江”4 分钟前
《【AIGC实战】Python+SDXL批量生成“英歌舞树莓香草布丁”IP,附保持角色一致性的代码逻辑》
开发语言·人工智能·python·prompt·aigc
张张123y7 分钟前
后端第十三课:大模型评测(Evals)从 0 到 1——看完面试能讲明白
后端·面试·职场和发展·大模型评测·evals
闻缺陷则喜何志丹9 分钟前
【C++特性及类库】多线程
开发语言·c++·多线程·语言特性·系统库
dtq042422 分钟前
C++3 - 分支语句if和string变量
开发语言·c++·学习
jimy122 分钟前
c++类成员访问只看静态类型
开发语言·c++
1560820721937 分钟前
银河麒麟 Qt 可执行文件打包说明
开发语言·驱动开发·qt
小蒜学长39 分钟前
基于数据挖掘的京东商城商品推荐系统设计与实现(代码+数据库+LW)
java·数据库·spring boot·后端·推荐系统·个性化推荐
SEO_juper41 分钟前
手写 llms.txt 验证器:让 AI 更愿意引用你的网站(GEO)
开发语言·人工智能·python·聚类·seo·独立站
CCCCCCCCharlie42 分钟前
从0到1:一个字节的磁盘之旅
linux·开发语言·c++