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

相关推荐
zzzll11117 分钟前
Spring Boot 入门指南:从零开始构建微服务
spring boot·后端·微服务
运维行者_27 分钟前
企业带宽监控工具实战:网络流量分析与异常排查的5个关键能力
运维·服务器·开发语言·网络·分布式·后端·php
Patrick在香港28 分钟前
Python爬虫框架实战:优雅抓取香港政府公开API数据的通用方案
java·开发语言·爬虫·python·ai编程·数据可视化·可用性测试
长栎34 分钟前
你用了五年的消息队列,不知道它背后站着中介者模式
后端
Gopher_HBo34 分钟前
路由注册:RouterGroup(routergroup.go)
后端
用户6081865279038 分钟前
Avalonia UI 控件样式定义的三种方式详解
后端
hey_sml38 分钟前
MySQL常用操作速记:从字符集到游标全解析
java·开发语言·mysql
广东程序猿小展38 分钟前
《007初露锋芒》风灵月影13项修改器下载 007初露锋芒修改器使用方法
开发语言·javascript·ecmascript
feng尘39 分钟前
volatile 可见性与内存屏障知识点
后端
SamDeepThinking41 分钟前
第3篇:企业级CAS单点登录实战-技术架构设计方案
后端·程序员·架构