Go 语言标准库中Channels,Goroutines详细功能介绍与示例

在 Go 语言中,Goroutines(协程)和 Channels(通道)是并发编程的核心组件。它们共同协作,简化了并发任务的管理和数据同步。以下通过详细示例说明它们的用法和常见模式。


1. Goroutines(协程)

Goroutine 是轻量级线程,由 Go 运行时调度,启动成本极低(通常仅几 KB 内存)。

基本用法

通过 go 关键字启动一个 Goroutine:

go 复制代码
package main

import (
    "fmt"
    "time"
)

func printNumbers() {
    for i := 1; i <= 3; i++ {
        fmt.Println("Number:", i)
        time.Sleep(100 * time.Millisecond)
    }
}

func printLetters() {
    for c := 'a'; c <= 'c'; c++ {
        fmt.Println("Letter:", string(c))
        time.Sleep(100 * time.Millisecond)
    }
}

func main() {
    go printNumbers() // 启动 Goroutine
    go printLetters()

    // 主 Goroutine 等待其他协程执行
    time.Sleep(1 * time.Second)
    fmt.Println("Main Goroutine 结束")
}

输出(顺序可能不同):

复制代码
Letter: a
Number: 1
Number: 2
Letter: b
Number: 3
Letter: c
Main Goroutine 结束

2. Channels(通道)

Channel 是类型化的管道,用于 Goroutines 之间的通信和同步。

基本用法
go 复制代码
func worker(done chan bool) {
    fmt.Println("Worker 开始工作...")
    time.Sleep(1 * time.Second)
    fmt.Println("Worker 完成工作")
    done <- true // 发送完成信号
}

func main() {
    done := make(chan bool) // 创建布尔型通道
    go worker(done)

    <-done // 阻塞,直到接收到数据
    fmt.Println("主程序收到完成信号")
}

输出:

复制代码
Worker 开始工作...
Worker 完成工作
主程序收到完成信号

3. 缓冲通道(Buffered Channels)

允许在没有接收者时缓存一定数量的数据。

go 复制代码
func main() {
    messages := make(chan string, 2) // 缓冲容量为 2

    messages <- "消息1" // 不阻塞(缓存未满)
    messages <- "消息2"

    fmt.Println(<-messages) // 输出: 消息1
    fmt.Println(<-messages) // 输出: 消息2
}

4. 通道方向(Channel Direction)

限制通道在函数中的使用方式(只读或只写)。

go 复制代码
// 只写通道参数
func sendData(ch chan<- string, msg string) {
    ch <- msg
}

// 只读通道参数
func receiveData(ch <-chan string) {
    fmt.Println("收到消息:", <-ch)
}

func main() {
    ch := make(chan string)
    go sendData(ch, "Hello")
    receiveData(ch)
}

输出:

复制代码
收到消息: Hello

5. Select 语句

监听多个通道操作,处理第一个就绪的通道。

go 复制代码
func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() {
        time.Sleep(1 * time.Second)
        ch1 <- "来自 ch1"
    }()

    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- "来自 ch2"
    }()

    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-ch1:
            fmt.Println(msg1)
        case msg2 := <-ch2:
            fmt.Println(msg2)
        }
    }
}

输出:

复制代码
来自 ch1
来自 ch2

6. 关闭通道与遍历通道

通过 close 关闭通道,通过 range 遍历通道数据。

go 复制代码
func produceNumbers(ch chan int) {
    for i := 0; i < 5; i++ {
        ch <- i
    }
    close(ch) // 关闭通道
}

func main() {
    ch := make(chan int)
    go produceNumbers(ch)

    // 循环读取直到通道关闭
    for num := range ch {
        fmt.Println("收到数字:", num)
    }
}

输出:

复制代码
收到数字: 0
收到数字: 1
收到数字: 2
收到数字: 3
收到数字: 4

7. 超时与错误处理

结合 select 和 time.After 实现超时控制。

go 复制代码
func main() {
    ch := make(chan string)

    go func() {
        time.Sleep(3 * time.Second)
        ch <- "数据"
    }()

    select {
    case res := <-ch:
        fmt.Println("收到数据:", res)
    case <-time.After(2 * time.Second):
        fmt.Println("超时!")
    }
}

输出:

复制代码
超时!

8. Worker Pool(工作池)

使用缓冲通道和多个 Goroutines 构建任务处理池。

go 复制代码
func worker(id int, jobs <-chan int, results chan<- int) {
    for job := range jobs {
        fmt.Printf("Worker %d 开始处理任务 %d\n", id, job)
        time.Sleep(1 * time.Second)
        results <- job * 2
    }
}

func main() {
    jobs := make(chan int, 10)
    results := make(chan int, 10)

    // 启动 3 个 Worker
    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }

    // 发送 5 个任务
    for j := 1; j <= 5; j++ {
        jobs <- j
    }
    close(jobs)

    // 收集结果
    for a := 1; a <= 5; a++ {
        <-results
    }
}

输出:

复制代码
Worker 1 开始处理任务 1
Worker 2 开始处理任务 2
Worker 3 开始处理任务 3
Worker 1 开始处理任务 4
Worker 2 开始处理任务 5

总结

  • Goroutines :
    • 通过 go 关键字启动。
    • 轻量级,适合高并发场景。
  • Channels :
    • 同步通信:无缓冲通道需发送和接收同时就绪。
    • 异步通信:缓冲通道允许暂存数据。
    • 使用 close 关闭通道,range 遍历通道数据。
  • 高级模式 :
    • select 多路监听。
    • 超时控制、工作池、只读/只写通道。
  • 注意事项 :
    • 避免死锁(如未关闭的通道或未接收的数据)。
    • 使用 sync.WaitGroup 等待多个 Goroutines 完成。
相关推荐
盛世宏博北京2 分钟前
档案库房恒温恒湿消毒净化一体机与3D可视化平台联动调试笔记
开发语言·php·温湿度·档案室
乐橙开放平台18 分钟前
离岗检测怎么实现?通过客流相机或 AI 摄像头识别岗位区域无人状态,再推送告警消息
人工智能·笔记·后端·数码相机·物联网·自动化·音视频
小徐不徐说9 小时前
序列化与反序列化:把 C++ 对象可靠地保存下来
开发语言·数据库·c++·php
ZhengEnCi9 小时前
模型训练完整流程:从数据准备到模型上线的 10 个关键步骤(全文可跑)
后端·算法
ragnwang9 小时前
Notes: Python pybind11 import C++ module
开发语言·c++·python
王中阳Go9 小时前
后端转 Agent 岗面试怎么答:8 个高频问题与应答口径
人工智能·后端·python·大语言模型
Wang's Blog9 小时前
Java 项目实战: 交友项目 - 搜索附近的功能概述和分析
java·开发语言·交友
程序猿DD9 小时前
OctaFuse Gateway 2.18.0:MCP 接入支持 OAuth,供应商粘性绑定可视化管理
后端
骑着蜗牛撵大象3279 小时前
大模型 Agent 的沙箱护栏:gVisor 隔离、出口白名单与审计闭环
java·开发语言·jvm·安全·agent沙箱·gvisor·microvm
Dxy123931021610 小时前
JS DES 加密详解
开发语言·javascript·arcgis