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 完成。
相关推荐
李长渊哦38 分钟前
深入理解 JavaScript 中的全局对象与 JSON 序列化
开发语言·javascript·json
若水晴空初如梦1 小时前
QT聊天项目DAY06
开发语言·qt
h汉堡3 小时前
C++入门基础
开发语言·c++·学习
橘猫云计算机设计3 小时前
基于Springboot的自习室预约系统的设计与实现(源码+lw+部署文档+讲解),源码可白嫖!
java·spring boot·后端·毕业设计
HtwHUAT3 小时前
实验四 Java图形界面与事件处理
开发语言·前端·python
鄃鳕3 小时前
QSS【QT】
开发语言·qt
汤姆_5113 小时前
【c语言】深度理解指针4——sizeof和strlen
c语言·开发语言
秋书一叶3 小时前
SpringBoot项目打包为window安装包
java·spring boot·后端
碎梦归途3 小时前
23种设计模式-结构型模式之外观模式(Java版本)
java·开发语言·jvm·设计模式·intellij-idea·外观模式
muyouking114 小时前
4.Rust+Axum Tower 中间件实战:从集成到自定义
开发语言·中间件·rust