0

0

Go语言中 select 语句的“饥饿”现象与解决方案

聖光之護

聖光之護

发布时间:2025-09-19 16:37:34

|

994人浏览过

|

来源于php中文网

原创

go语言中 select 语句的“饥饿”现象与解决方案

这段摘要概括了本文的核心内容:Go 语言 select 语句在使用时可能因为 busy loop 导致某些 case 分支长时间无法被执行,称为“饥饿”现象。通过一个 time.Ticker 的例子解释了原因,并提供了 runtime.Gosched() 的解决方案。同时提醒开发者在涉及 I/O 或其他调度器触发场景下谨慎评估 select 语句的行为。

理解 Go 语言 select 语句的“饥饿”现象

在使用 Go 语言编写并发程序时,select 语句是一个非常重要的工具,它允许我们同时监听多个 channel 上的操作,并在其中一个 channel 可用时执行相应的代码块。然而,如果不小心使用 select 语句,可能会遇到“饥饿”现象,即某些 case 分支长时间无法被执行。

一个典型的例子是使用 time.Ticker 来周期性地执行某些任务,并将其与 select 语句结合使用:

package main

import (
    "fmt"
    "time"
    "runtime"
)

func main() {
    rt := time.NewTicker(time.Second / 60)
    defer rt.Stop() // 确保程序退出时停止 ticker

    for {
        select {
        case <-rt.C:
            fmt.Println("time")
        default:
            // 一些默认操作
        }
    }
}

在这个例子中,我们期望 time.Ticker 每隔 1/60 秒向 channel rt.C 发送一个值,从而触发 select 语句的第一个 case 分支。然而,如果没有其他措施,程序很可能会陷入一个 busy loop,不断地执行 default 分支,而

立即学习go语言免费学习笔记(深入)”;

为什么会出现“饥饿”现象?

出现这种现象的原因在于 Go 语言的调度器是协作式的。这意味着 Goroutine 只有在主动放弃 CPU 时间片时,其他 Goroutine 才能获得运行的机会。在上面的例子中,for 循环一直在运行,并且 select 语句的 default 分支也在快速执行。如果 default 分支没有进行任何 I/O 操作或者其他可以触发调度器的操作,那么 time.Ticker 所在的 Goroutine 就没有机会运行,也就无法向 rt.C 发送数据。

换句话说,select 语句陷入了 busy loop,它一直在检查 rt.C 是否有数据,但由于 time.Ticker 没有机会运行,rt.C 永远是空的。

如何解决“饥饿”现象?

解决“饥饿”现象的关键在于让 select 语句能够主动让出 CPU 时间片,给其他 Goroutine 运行的机会。一种简单有效的方法是使用 runtime.Gosched() 函数:

人民网AIGC-X
人民网AIGC-X

国内科研机构联合推出的AI生成内容检测工具

下载
package main

import (
    "fmt"
    "time"
    "runtime"
)

func main() {
    rt := time.NewTicker(time.Second / 60)
    defer rt.Stop()

    for {
        select {
        case <-rt.C:
            fmt.Println("time")
        default:
            runtime.Gosched() // 主动让出 CPU 时间片
            // 一些默认操作
        }
    }
}

runtime.Gosched() 函数的作用是让当前 Goroutine 放弃 CPU 时间片,让调度器重新调度其他 Goroutine。这样,time.Ticker 所在的 Goroutine 就可以获得运行的机会,从而向 rt.C 发送数据,使得 select 语句的第一个 case 分支能够被执行。

另一种方法是使用 time.Sleep() 函数,让当前 Goroutine 休眠一段时间:

package main

import (
    "fmt"
    "time"
)

func main() {
    rt := time.NewTicker(time.Second / 60)
    defer rt.Stop()

    for {
        select {
        case <-rt.C:
            fmt.Println("time")
        default:
            time.Sleep(time.Millisecond) // 休眠 1 毫秒
            // 一些默认操作
        }
    }
}

time.Sleep() 函数会让当前 Goroutine 休眠指定的时间,从而让其他 Goroutine 获得运行的机会。

注意事项

虽然 runtime.Gosched() 和 time.Sleep() 都可以解决“饥饿”现象,但它们的使用需要谨慎。过度使用 runtime.Gosched() 会导致频繁的 Goroutine 切换,降低程序的性能。而 time.Sleep() 则可能会引入不必要的延迟。

在实际开发中,应该根据具体情况选择合适的解决方案。如果 default 分支需要执行一些耗时的操作,可以考虑使用 runtime.Gosched() 让出 CPU 时间片。如果 default 分支的操作比较简单,可以考虑使用 time.Sleep() 休眠一段时间。

此外,还需要注意 select 语句中各个 case 分支的优先级。如果某些 case 分支的条件总是满足,那么其他 case 分支可能会一直无法被执行。在这种情况下,需要重新设计 select 语句的逻辑,避免出现优先级不平衡的问题。

总结

select 语句是 Go 语言中一个强大的并发工具,但如果不小心使用,可能会遇到“饥饿”现象。理解“饥饿”现象的原因,并掌握相应的解决方案,可以帮助我们编写出更加健壮和高效的并发程序。在使用 select 语句时,需要注意以下几点:

  • 避免 busy loop,让 select 语句能够主动让出 CPU 时间片。
  • 根据具体情况选择合适的解决方案,如 runtime.Gosched() 或 time.Sleep()。
  • 注意 select 语句中各个 case 分支的优先级,避免出现优先级不平衡的问题。

相关专题

更多
Go中Type关键字的用法
Go中Type关键字的用法

Go中Type关键字的用法有定义新的类型别名或者创建新的结构体类型。本专题为大家提供Go相关的文章、下载、课程内容,供大家免费下载体验。

233

2023.09.06

go怎么实现链表
go怎么实现链表

go通过定义一个节点结构体、定义一个链表结构体、定义一些方法来操作链表、实现一个方法来删除链表中的一个节点和实现一个方法来打印链表中的所有节点的方法实现链表。

444

2023.09.25

go语言编程软件有哪些
go语言编程软件有哪些

go语言编程软件有Go编译器、Go开发环境、Go包管理器、Go测试框架、Go文档生成器、Go代码质量工具和Go性能分析工具等。本专题为大家提供go语言相关的文章、下载、课程内容,供大家免费下载体验。

246

2023.10.13

0基础如何学go语言
0基础如何学go语言

0基础学习Go语言需要分阶段进行,从基础知识到实践项目,逐步深入。php中文网给大家带来了go语言相关的教程以及文章,欢迎大家前来学习。

693

2023.10.26

Go语言实现运算符重载有哪些方法
Go语言实现运算符重载有哪些方法

Go语言不支持运算符重载,但可以通过一些方法来模拟运算符重载的效果。使用函数重载来模拟运算符重载,可以为不同的类型定义不同的函数,以实现类似运算符重载的效果,通过函数重载,可以为不同的类型实现不同的操作。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

191

2024.02.23

Go语言中的运算符有哪些
Go语言中的运算符有哪些

Go语言中的运算符有:1、加法运算符;2、减法运算符;3、乘法运算符;4、除法运算符;5、取余运算符;6、比较运算符;7、位运算符;8、按位与运算符;9、按位或运算符;10、按位异或运算符等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

228

2024.02.23

go语言开发工具大全
go语言开发工具大全

本专题整合了go语言开发工具大全,想了解更多相关详细内容,请阅读下面的文章。

280

2025.06.11

go语言引用传递
go语言引用传递

本专题整合了go语言引用传递机制,想了解更多相关内容,请阅读专题下面的文章。

158

2025.06.26

php与html混编教程大全
php与html混编教程大全

本专题整合了php和html混编相关教程,阅读专题下面的文章了解更多详细内容。

3

2026.01.13

热门下载

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

精品课程

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

共32课时 | 3.7万人学习

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号