当前位置: 首页 > news >正文

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

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


1. Goroutines(协程)

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

基本用法

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

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 之间的通信和同步。

基本用法
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)

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

func main() {
    messages := make(chan string, 2) // 缓冲容量为 2

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

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

4. 通道方向(Channel Direction)

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

// 只写通道参数
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 语句

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

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 遍历通道数据。

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 实现超时控制。

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 构建任务处理池。

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 完成。

相关文章:

  • 2025AWE观察:“无AI不家电”,但“AI”还是“AL”仍是个问题
  • OpenCV图像拼接(10)用于实现图像拼接过程中的时间流逝(timelapse)效果的一个类cv::detail::Timelapser
  • 【产品小白】产品视角的RAG
  • 进程状态:Linux的幕后指挥管理,穿越操作系统进程的静与动
  • 自然语言处理|高效法律助手:AI如何解析合同条款?
  • ChatBI的流程图
  • 深入探究成都国际数字影像产业园的运营模式
  • 一周掌握Flutter开发--9. 与原生交互(上)
  • Spring-boot引入nacos但未生效
  • 如何使用RK平台的spi驱动 spidev
  • 检查指定的IP地址和端口号是否可以连接
  • Vue与Supabase交互文档
  • 【MySQL基础】数据库及表基本操作
  • HarmonyOS NEXT——【鸿蒙原生应用加载Web页面】
  • ThreadLocal与Cookie + Session?
  • Audacity Nyquist插件开发:定义输入框和获取用户输入
  • Unity 运行时更换Animator状态里的动画剪辑
  • Docker部署minio,SSL证书问题与两个解决方案
  • 【数据结构】栈 与【LeetCode】20.有效的括号详解
  • Spring Boot分布式项目实战:装饰模式的正确打开方式
  • 四川省工程建设信息官方网站/qq群排名优化软件
  • 彩票站自己做网站/360指数
  • 网站自适应是什么做的/网站设计公司苏州
  • 品牌网站建设联系方式/公司官网怎么做
  • web 网站做横道图/做企业推广
  • html网站的直播怎么做的/百度网盘搜索引擎入口