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 完成。
相关推荐
杨DaB22 分钟前
【SpringMVC】拦截器,实现小型登录验证
java·开发语言·后端·servlet·mvc
近津薪荼22 分钟前
c++详解(宏与内联函数,nullptr)
开发语言·c++
天若有情6736 小时前
【python】Python爬虫入门教程:使用requests库
开发语言·爬虫·python·网络爬虫·request
Hello.Reader6 小时前
用 Go Typed Client 快速上手 Elasticsearch —— 从建索引到聚合的完整实战
elasticsearch·golang·jenkins
寒水馨7 小时前
Java 17 新特性解析与代码示例
java·开发语言·jdk17·新特性·java17
启山智软7 小时前
选用Java开发商城的优势
java·开发语言
秃然想通7 小时前
掌握Python三大语句:顺序、条件与循环
开发语言·python·numpy
##学无止境##7 小时前
Maven 从入门到精通:Java 项目构建与依赖管理全解析(上)
java·开发语言·maven
努力的小雨7 小时前
还在为调试提示词头疼?一个案例教你轻松上手!
后端