0

0

Go 协程阻塞问题详解:原因、解决方法与避免策略

聖光之護

聖光之護

发布时间:2025-10-21 09:26:02

|

700人浏览过

|

来源于php中文网

原创

go 协程阻塞问题详解:原因、解决方法与避免策略

本文旨在深入解析 Go 协程(goroutine)阻塞问题,通过具体示例代码,详细阐述了协程阻塞的原因,即 Go 采用的协作式调度机制。同时,介绍了协程让出 CPU 的常见场景,以及手动让出 CPU 的方法。最后,讨论了 `GOMAXPROCS` 的作用,并强调了其在解决协程阻塞问题上的局限性,帮助开发者更好地理解和避免 Go 协程阻塞,提升程序性能。

Go 语言的并发模型基于 Goroutine,这是一种轻量级的线程,可以高效地执行并发任务。然而,由于 Go 采用的是协作式调度,不当的使用会导致 Goroutine 阻塞,从而影响程序的整体性能。

协作式调度机制

Go 的 Goroutine 调度器采用协作式调度,这意味着 Goroutine 需要主动让出 CPU 才能使其他 Goroutine 获得执行机会。如果一个 Goroutine 长时间占用 CPU 而不进行任何 I/O 操作或显式地让出 CPU,就会导致其他 Goroutine 无法得到执行,从而造成阻塞。

阻塞示例

以下代码展示了一个 Goroutine 阻塞导致其他 Goroutine 无法执行的例子:

package main

import (
    "fmt"
    "time"
)

func main() {
    timeout := make(chan int)
    go func() {
        time.Sleep(time.Second)
        timeout <- 1
    }()

    res := make(chan int)
    go func() {
        for {
        }
        res <- 1
    }()
    select {
    case <-timeout:
        fmt.Println("timeout")
    case <-res:
        fmt.Println("res")
    }
}

在这个例子中,一个 Goroutine 进入了一个无限循环,并且没有执行任何 I/O 操作或让出 CPU 的操作。这导致 timeout channel 无法接收到值,select 语句一直阻塞在 timeout case 上,程序永远无法输出 "timeout"。

Goroutine 让出 CPU 的场景

以下是一些 Goroutine 会让出 CPU 的常见场景:

  • 无缓冲 Channel 的发送/接收操作: 当 Goroutine 尝试向一个无缓冲的 Channel 发送数据时,它会阻塞,直到有另一个 Goroutine 从该 Channel 接收数据。同样,当 Goroutine 尝试从一个无缓冲的 Channel 接收数据时,它会阻塞,直到有另一个 Goroutine 向该 Channel 发送数据。
  • 系统调用: 包括文件和网络 I/O 操作,例如读取和写入文件或网络连接。
  • 内存分配: 当 Goroutine 需要分配内存时,它可能会让出 CPU,以便垃圾回收器可以运行。
  • time.Sleep() 调用: time.Sleep() 函数会使 Goroutine 暂停指定的时间,从而让出 CPU。
  • runtime.Gosched() 调用: runtime.Gosched() 函数可以显式地让 Goroutine 让出 CPU,以便其他 Goroutine 可以运行。

手动让出 CPU

在一些 CPU 密集型的循环中,可以通过调用 runtime.Gosched() 函数来手动让出 CPU,避免 Goroutine 长时间占用 CPU 导致其他 Goroutine 无法执行。

RecoveryFox AI
RecoveryFox AI

AI驱动的数据恢复、文件恢复工具

下载

例如:

package main

import (
    "fmt"
    "runtime"
)

func main() {
    done := make(chan bool)

    go func() {
        for i := 0; i < 1000000000; i++ {
            if i%1000000 == 0 {
                runtime.Gosched() // 手动让出 CPU
            }
        }
        fmt.Println("Worker goroutine finished")
        done <- true
    }()

    // Main goroutine does some work
    for i := 0; i < 5; i++ {
        fmt.Println("Main goroutine working...", i)
        runtime.Gosched() // 可选:主协程也让出CPU
    }

    <-done // 等待 worker goroutine 完成
    fmt.Println("Program finished")
}

在这个例子中,runtime.Gosched() 函数被用于在 CPU 密集型的循环中手动让出 CPU,以便其他 Goroutine 可以运行。

GOMAXPROCS 的作用与局限性

GOMAXPROCS 环境变量用于设置可以同时执行 Goroutine 的最大 CPU 核心数。虽然增加 GOMAXPROCS 的值可以使更多的 Goroutine 并行执行,但它并不能解决 Goroutine 阻塞的问题。

即使有多个 CPU 核心可用,如果一个 Goroutine 长时间占用 CPU 而不进行任何 I/O 操作或显式地让出 CPU,其他 Goroutine 仍然无法得到执行。此外,垃圾回收器在运行时会停止所有 Goroutine,如果 CPU 密集型的 Goroutine 始终不让出 CPU,垃圾回收器可能会被无限期地阻塞。

总结与建议

理解 Go 的协作式调度机制是避免 Goroutine 阻塞的关键。在编写并发程序时,应该注意以下几点:

  • 避免长时间占用 CPU 的循环,尽量使用 I/O 操作或显式地让出 CPU。
  • 合理使用 Channel 进行 Goroutine 之间的通信和同步。
  • 根据实际情况调整 GOMAXPROCS 的值,但不要期望它能解决所有 Goroutine 阻塞的问题。
  • 使用 runtime.Gosched() 函数在 CPU 密集型的循环中手动让出 CPU。
  • 使用性能分析工具来检测和诊断 Goroutine 阻塞问题。

通过遵循这些建议,可以编写出高效、稳定的 Go 并发程序。

热门AI工具

更多
DeepSeek
DeepSeek

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

豆包大模型
豆包大模型

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

通义千问
通义千问

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

腾讯元宝
腾讯元宝

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

文心一言
文心一言

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

讯飞写作
讯飞写作

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

即梦AI
即梦AI

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

ChatGPT
ChatGPT

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

相关专题

更多
线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

503

2023.08.10

Golang channel原理
Golang channel原理

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

248

2025.11.14

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

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

344

2025.11.17

俄罗斯Yandex引擎入口
俄罗斯Yandex引擎入口

2026年俄罗斯Yandex搜索引擎最新入口汇总,涵盖免登录、多语言支持、无广告视频播放及本地化服务等核心功能。阅读专题下面的文章了解更多详细内容。

165

2026.01.28

包子漫画在线官方入口大全
包子漫画在线官方入口大全

本合集汇总了包子漫画2026最新官方在线观看入口,涵盖备用域名、正版无广告链接及多端适配地址,助你畅享12700+高清漫画资源。阅读专题下面的文章了解更多详细内容。

34

2026.01.28

ao3中文版官网地址大全
ao3中文版官网地址大全

AO3最新中文版官网入口合集,汇总2026年主站及国内优化镜像链接,支持简体中文界面、无广告阅读与多设备同步。阅读专题下面的文章了解更多详细内容。

73

2026.01.28

php怎么写接口教程
php怎么写接口教程

本合集涵盖PHP接口开发基础、RESTful API设计、数据交互与安全处理等实用教程,助你快速掌握PHP接口编写技巧。阅读专题下面的文章了解更多详细内容。

2

2026.01.28

php中文乱码如何解决
php中文乱码如何解决

本文整理了php中文乱码如何解决及解决方法,阅读节专题下面的文章了解更多详细内容。

4

2026.01.28

Java 消息队列与异步架构实战
Java 消息队列与异步架构实战

本专题系统讲解 Java 在消息队列与异步系统架构中的核心应用,涵盖消息队列基本原理、Kafka 与 RabbitMQ 的使用场景对比、生产者与消费者模型、消息可靠性与顺序性保障、重复消费与幂等处理,以及在高并发系统中的异步解耦设计。通过实战案例,帮助学习者掌握 使用 Java 构建高吞吐、高可靠异步消息系统的完整思路。

8

2026.01.28

热门下载

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

精品课程

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

共32课时 | 4.3万人学习

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号