0

0

Go 并发编程中的 Goroutine 调度与阻塞问题

心靈之曲

心靈之曲

发布时间:2025-08-31 21:01:00

|

452人浏览过

|

来源于php中文网

原创

go 并发编程中的 goroutine 调度与阻塞问题

本文探讨了 Go 并发编程中一个常见的“海森堡 Bug”,即在某些情况下,向标准输出打印内容会导致程序行为发生改变。通过分析一个模拟理发师问题的示例,解释了 Go 调度器的运作机制,以及如何通过更合理的方式进行非阻塞的 channel 操作,避免潜在的竞争条件。

在 Go 语言的并发编程中,Goroutine 的调度是一个核心概念。理解 Goroutine 的调度方式对于编写高效且可靠的并发程序至关重要。一个常见的困惑是,为什么在一段并发代码中添加 fmt.Println 语句后,程序的行为会发生改变,甚至从阻塞状态恢复正常?

Goroutine 调度机制

Go 语言的调度器负责在多个 Goroutine 之间分配 CPU 时间。Goroutine 只有在进行系统调用或者阻塞的 channel 操作时,才会让出 CPU 的执行权。fmt.Println 函数会触发系统调用,这使得当前 Goroutine 有机会让出 CPU,让其他 Goroutine 获得执行机会。

在没有 fmt.Println 的情况下,如果某个 Goroutine 一直在执行计算密集型任务,而没有进行任何阻塞操作,那么它可能会一直占用 CPU,导致其他 Goroutine 无法得到执行。这可能导致程序出现“饿死”现象,即某些 Goroutine 永远无法运行。

示例:理发师问题

考虑一个简化的理发师问题:

package main

import "fmt"

func customer(id int, shop chan<- int) {
    // 进入理发店,如果还有座位,否则离开
    // fmt.Println("取消注释这行代码,程序就能正常工作")
    if len(shop) < cap(shop) {
        shop <- id
    }
}

func barber(shop <-chan int) {
    // 为进入理发店的顾客理发
    for {
        fmt.Println("理发师为顾客", <-shop, "理发")
    }
}

func main() {
    shop := make(chan int, 5) // 五个座位
    go barber(shop)
    for i := 0; ; i++ {
        customer(i, shop)
    }
}

在这个例子中,customer 函数模拟顾客进入理发店,如果理发店有空位,则将顾客 ID 发送到 shop channel 中。barber 函数模拟理发师,从 shop channel 中接收顾客 ID 并为其理发。

TemPolor
TemPolor

AI音乐生成器,一键创作免版税音乐

下载

如果没有注释掉 customer 函数中的 fmt.Println 语句,程序可以正常运行。但是,如果注释掉该语句,barber 函数可能永远无法接收到顾客 ID,导致程序阻塞。

这是因为 customer 函数在没有 fmt.Println 的情况下,可能会一直快速地向 shop channel 发送顾客 ID,而 barber 函数没有机会从 shop channel 中接收顾客 ID。当 shop channel 满时,customer 函数会阻塞,但由于 barber 函数没有机会运行,因此 shop channel 永远无法被清空,导致程序死锁。

更安全的非阻塞 Channel 操作

在 customer 函数中使用 len(shop)

func customer(id int, shop chan<- int) {
    // 进入理发店,如果还有座位,否则离开
    select {
    case shop <- id:
    default:
    }
}

select 语句会尝试向 shop channel 发送顾客 ID。如果 shop channel 已满,则会执行 default 分支,而不会阻塞。这种方式更加安全,可以避免潜在的竞争条件。

总结

理解 Go 语言的 Goroutine 调度机制对于编写健壮的并发程序至关重要。fmt.Println 语句看似无害,但实际上会影响 Goroutine 的调度。在编写并发程序时,需要注意避免长时间占用 CPU 的计算密集型任务,并使用更安全的方式进行非阻塞的 channel 操作,以避免潜在的竞争条件和死锁。

相关文章

编程速学教程(入门课程)
编程速学教程(入门课程)

编程怎么学习?编程怎么入门?编程在哪学?编程怎么学才快?不用担心,这里为大家提供了编程速学教程(入门课程),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
Golang channel原理
Golang channel原理

本专题整合了Golang channel通信相关介绍,阅读专题下面的文章了解更多详细内容。

248

2025.11.14

golang channel相关教程
golang channel相关教程

本专题整合了golang处理channel相关教程,阅读专题下面的文章了解更多详细内容。

344

2025.11.17

default gateway怎么配置
default gateway怎么配置

配置default gateway的步骤:1、了解网络环境;2、获取路由器IP地址;3、登录路由器管理界面;4、找到并配置WAN口设置;5、配置默认网关;6、保存设置并退出;7、检查网络连接是否正常。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

223

2023.12.07

go语言 注释编码
go语言 注释编码

本专题整合了go语言注释、注释规范等等内容,阅读专题下面的文章了解更多详细内容。

30

2026.01.31

go语言 math包
go语言 math包

本专题整合了go语言math包相关内容,阅读专题下面的文章了解更多详细内容。

17

2026.01.31

go语言输入函数
go语言输入函数

本专题整合了go语言输入相关教程内容,阅读专题下面的文章了解更多详细内容。

16

2026.01.31

golang 循环遍历
golang 循环遍历

本专题整合了golang循环遍历相关教程,阅读专题下面的文章了解更多详细内容。

5

2026.01.31

Golang人工智能合集
Golang人工智能合集

本专题整合了Golang人工智能相关内容,阅读专题下面的文章了解更多详细内容。

5

2026.01.31

2026赚钱平台入口大全
2026赚钱平台入口大全

2026年最新赚钱平台入口汇总,涵盖任务众包、内容创作、电商运营、技能变现等多类正规渠道,助你轻松开启副业增收之路。阅读专题下面的文章了解更多详细内容。

253

2026.01.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go 教程
Go 教程

共32课时 | 4.5万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号