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. 超时与错误处理

结合 selecttime.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 完成。
相关推荐
红烧大青虫8 小时前
HarmonyOS应用开发实战:小事记 - UIAbility 的冷启动/热启动/后台启动三种场景与 launchParam 解析
后端·华为·harmonyos·鸿蒙系统
码事漫谈8 小时前
AI Token 缓存:命中省 10 倍,不命中白扔钱
后端
CRMEB系统商城8 小时前
开源自建还是SaaS订阅?算一笔3年经济账
java·大数据·开发语言·开源
65岁退休Coder8 小时前
LangChain v1.3.4 笔记 - 04 Agent 中间件
后端
keyipatience9 小时前
线程栈与TLS和线程互斥
java·linux·服务器·开发语言·ubuntu
神奇小汤圆9 小时前
一个接口多个实现,Spring 怎么"适配多场景"?
后端
花开彼岸天~9 小时前
鸿蒙原生开发手记:徒步迹 - 自定义组件开发规范
后端·华为·harmonyos·鸿蒙系统
我的xiaodoujiao9 小时前
快速学习Python基础知识详细图文教程9--函数进阶
开发语言·python·学习·测试工具
爱喝水的鱼丶10 小时前
SAP-ABAP:ALV通用封装实践——搭建可复用的ALV开发工具类,减少80%重复代码
开发语言·性能优化·sap·abap·erp·alv
脱胎换骨-军哥10 小时前
C++/Rust无缝互操作:混合系统新常态
开发语言·c++·rust