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

Go语言-初学者日记(六) 并发编程

Go 语言内置并发模型,被誉为“并发界的扛把子”。它的并发不是线程、不是回调地狱,而是轻量、优雅的 goroutine + channel 模式。

这一篇带你一步步掌握 Go 并发编程:从 goroutine 到 channel、select、context、sync,写出高并发高性能代码不是梦!


🧵 一、开启并发的第一步:goroutine

func sayHello() {
    fmt.Println("Hello from goroutine")
}

go sayHello()
fmt.Println("Main finished")

📌 使用 go 关键字创建 goroutine。它是 Go 的轻量线程:

  • 每个 goroutine 栈空间初始仅 2KB,远小于线程
  • Go runtime 自动调度,使用 M:N 调度模型(goroutine 与系统线程动态映射)

⚠️ 注意:如果 main() 提前退出,goroutine 没机会执行。

time.Sleep(time.Second)

📬 二、goroutine 通信神器:channel

ch := make(chan string)

go func() {
    ch <- "数据来啦!"
}()

msg := <-ch
fmt.Println(msg)

✅ channel 特点:

  • 类型安全
  • 无缓冲时:发送和接收会互相阻塞(同步通信)
  • channel 是“通信的内存”,不是“共享的内存”

🔁 三、带缓冲的 channel

ch := make(chan int, 2) // 缓冲区大小 2
ch <- 1
ch <- 2
fmt.Println(<-ch)
fmt.Println(<-ch)

🧠 使用场景:

  • 任务管道
  • 控制速率(限流)
  • 缓冲写入日志、数据等

🔀 四、select:监听多个 channel

select {
case msg1 := <-ch1:
    fmt.Println("收到1:", msg1)
case msg2 := <-ch2:
    fmt.Println("收到2:", msg2)
default:
    fmt.Println("啥也没收到")
}
  • 类似 switch,但用于 channel
  • 可以实现超时、轮询、优先级控制等逻辑

📌 配合 time.After() 做超时控制:

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

⏳ 五、context:优雅地取消 goroutine

ctx, cancel := context.WithCancel(context.Background())

go func(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            fmt.Println("收到取消信号,退出")
            return
        default:
            fmt.Println("执行中...")
            time.Sleep(500 * time.Millisecond)
        }
    }
}(ctx)

time.Sleep(2 * time.Second)
cancel()

📌 常见使用场景:

  • HTTP 请求取消
  • 数据库操作超时
  • 子任务取消控制

函数:

名称用途
WithCancel手动调用 cancel()
WithTimeout一段时间后自动取消
WithDeadline指定时间点取消

🧱 六、sync 包:共享数据的并发控制

✅ WaitGroup:等待一组 goroutine 完成
var wg sync.WaitGroup

for i := 0; i < 5; i++ {
    wg.Add(1)
    go func(i int) {
        defer wg.Done()
        fmt.Println("任务", i)
    }(i)
}

wg.Wait()
✅ Mutex:互斥锁,保护共享资源
var mu sync.Mutex
var count int

for i := 0; i < 10; i++ {
    go func() {
        mu.Lock()
        count++
        mu.Unlock()
    }()
}
✅ sync.Once:只执行一次(懒加载)
var once sync.Once
once.Do(func() {
    fmt.Println("只执行一次")
})

⚙️ 七、channel 关闭与判断

close(ch)
val, ok := <-ch
if !ok {
    fmt.Println("通道已关闭")
}
  • 关闭后不能再写入,否则 panic
  • 读已关闭通道会返回零值和 false

🧠 八、小结与对比

技术点用途
goroutine创建并发执行单元
channel安全通信管道
select多路 channel 选择
context协程生命周期控制
sync互斥、等待、只执行一次等

💡 实战推荐练习

  • ✅ 写一个并发爬虫,使用 goroutine + channel 控制并发数量
  • ✅ 用 context 控制下载任务的取消
  • ✅ 构建一个日志服务,带缓冲 channel + goroutine 写入日志文件
  • ✅ 使用 WaitGroup 模拟并发任务同步收集结果

🏁 下一篇预告

👉 Go语言-初学者日记(七):项目实战篇:用 Go 写一个 RESTful API 服务!

相关文章:

  • Nginx-keepalived-高可用
  • cpp经典数论问题
  • celsius与Fahrenheit
  • C++ STL 详解 ——list 的深度解析与实践指南
  • leetcode120.三角形最小路径
  • 小刚说C语言刷题——第16讲 switch语句
  • C++11详解
  • Spring Boot 与 TDengine 的深度集成实践(三)
  • 利用C++编写操作OpenCV常用操作
  • 编程速递-Delphi is 30 Delphi诞生30周年!
  • MySQL-SQL-DQL语句、DQL基本查询、DQL条件查询、DQL分组查询、聚合函数、DQL排序查询、DQL分页查询
  • 【勒让德公式】欧拉筛-阶乘分解
  • 【硬件开发技巧】如何通过元器件丝印反查型号
  • vector模拟实现(2)
  • 蓝桥杯2024年第十五届省赛真题-拔河
  • 专栏:区块链入门到放弃查看目录
  • el-tabs添加按钮增加点击禁止样式
  • ubuntu 配置固定ip
  • getline(cin, )
  • Qt音频输出:QAudioOutput详解与示例
  • 慈溪怎么做网站/百度信息流代理
  • 网站建设实验报告手写/网站优化排名的方法
  • 金湖县建设工程质量监督网站/市场营销培训
  • 弹幕网站如何做/找个免费网站这么难吗
  • 做平面设计兼职的网站/建立网站的流程
  • 山东兴华建设集团有限公司网站拼音/怎么搭建网站