news 2026/2/25 3:28:43

从一次「登录阻塞」说起:我终于理解了 goroutine

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从一次「登录阻塞」说起:我终于理解了 goroutine

最近再写一个Go TCP 网络聊天室,服务端有登录、注册功能。
一开始写得很顺,功能也都能跑,但很快我发现了一个致命问题:
当一个客户端在登录/注册时,其他客户端居然完全没法操作了。

这不是 bug,而是我并没有真正理解 goroutine 的使用场景。

一."理所当然却错误"的服务端写法

先来看一个非常符合新手直觉的TCP服务端结构

listener, _ := net.Listen("tcp", ":8888") for { conn, _ := listener.Accept() handleConn(conn) }

然后在handleConn里面

func handleConn(conn net.Conn) { for { msg := readMsg(conn) if msg.Type == "login" { doLogin(conn) } if msg.Type == "register" { doRegister(conn) } } }

当时并没有考虑到 :当一个用户进行登录注册,在阻塞的时候,其他的客户端并不能自然向下进行

二.问题的本质

关键点就在于: **Accept()**后面的逻辑如果不并发,那么整个服务器就是串行的
我原本的程序逻辑实际上是这样的

接收连接A →处理A的登录(阻塞) →A没有处理完 →B永远等不到处理机会

而登录/注册的阻塞有很多种:

  • conn.Read()可能因为网络问题阻塞
  • 密码验证
  • 用户输入慢,失败重试–只要有一个人在输入密码,后面所有人陪着等
  • 数据库访问

聊天室中,不止是聊天需要并发,登录注册同样需要

三.修改方案

给每个连接都启动一个goroutine

for { conn, err := listener.Accept() if err != nil { continue } go handleConn(conn) }

这样修改完之后,整体的逻辑就变成了这样:

主 goroutine:只负责 Accept 新连接 | |-- goroutine #1 → 客户端 A(登录) |-- goroutine #2 → 客户端 B(聊天) |-- goroutine #3 → 客户端 C(注册) |-- goroutine #4 → 客户端 D(登录)

四.goroutine在聊天室中的"真实分工"

  1. 连接 goroutine
go handleConn(conn) // 一个客户端一个goroutine // 生命周期 = 连接生命周期
  1. 读写解耦 goroutine
go readLoop(conn) go writeLoop(conn) // 读写不阻塞,写不影响读
  1. 登录注册 goroutine
go func() { user := login(conn) loginResultChan <- user }() // 登录校验不阻塞消息接收
  1. 心跳检测/超时踢人 goroutine
go func() { ticker := time.NewTicker(30 * time.Second) for range ticker.C { checkAliveUsers() } }() // 定时任务不能阻塞主流程

完全可以把这些需要用的放在一个控制整体流程的函数,然后给这个函数起一个协程

五.goroutine 带来的第二层问题

当你开始大量使用 goroutine,很快会遇到这些新问题:

  1. goroutine 泄露
go func() { msg := <-ch // 客户端已经断开 }()

客户端已经断开,goroutine会永远挂着,造成goroutine泄露
解决方案:使用context包管理协程生命周期;连接关闭时 close channel

  1. 在线用户 map并发读
onlineUsers[userID] = conn // 并发不安全

解决方案: 加锁:sync.Mutex 或者channel + 单 goroutine 管理状态
3. 广播消息阻塞

for _, conn := range conns { conn.Write(msg) // 一个慢客户端拖垮全体 }

每个用户一个发送 goroutine;发送channel

六.协程相关内容回顾

  1. 基本概念
    定义:Go 语言中的轻量级并发执行单元,由 Go runtime 调度
    特点
    • 创建成本极低(初始栈 ~2KB,可动态增长)
    • 由 Go 调度器而非操作系统线程管理
    • 可以同时运行成千上万个 goroutine
      基本用法:
go func() { fmt.Println("Hello, goroutine!") }()
  1. goroutine 生命周期
    1.创建(调用 go func() {})
    2. 可运行(等待被调度)
    3. 运行(实际在 CPU 上执行)
    4. 阻塞(IO、channel、锁等)
    5. 结束(完成或被取消)

阻塞的 goroutine 不占用 OS 线程,Go runtime 会调度其他 goroutine 继续执行。

  1. goroutine 与线程的关系
对象创建成本调度者栈大小
OS 线程操作系统MB 级
Goroutine极低Go runtime~2KB,可动态增长

多个 goroutine 可以共享少量线程,由 Go runtime 调度
并发 ≠ 并行:

  • 并发:逻辑上同时执行
  • 并行:物理上同时执行(受 CPU 核心限制)
  1. GMP 调度模型(Go核心原理)
  • G (Goroutine):执行单元
  • M (Machine / OS 线程):执行 G 的实际线程
  • P (Processor):调度器,控制可运行 goroutine 队列

调度流程:

  1. 新建 goroutine → 放入 P 的本地队列
  2. M 从 P 的队列取 G 执行
  3. G 阻塞 → 挂起,M 可执行其他 G
  4. 空闲 P 会“工作窃取”其他 P 的队列

并行度由 runtime.GOMAXPROCS(n) 控制(默认等于 CPU 核心数)

  1. goroutine 常见问题
  • 泄露:goroutine 永远阻塞,未退出
    解决:context + close(channel) + 检查阻塞点
  • main goroutine提前退出,所有的子 goroutine 会被强制结束
    解决: 阻塞 main(WaitGroup / select / channel)
  • 闭包变量捕获陷阱
for i := 0; i < 3; i++ { go func() { fmt.Println(i) }() // ❌ go func(i int) { fmt.Println(i) }(i) // ✅ }
  • channel 阻塞:发送方满,接收方空时阻塞
    解决:缓冲 channel / 超时 / 丢弃策略
  • 无限创建 goroutine:容易OOM
    解决:控制数量 / worker pool

OOM 是 Out Of Memory 的缩写,表示系统内存被占满;Go runtime会抛出panic,程序崩掉
典型表现:fatal error: runtime: out of memory

  1. goroutine使用建议
  • 原则:凡是可能阻塞的逻辑,都用 goroutine 隔离。
  • 不要滥用:短小、非阻塞、一次性操作不必 goroutine。
  • 确保退出路径:每个 goroutine 都应能被取消或结束。
  • 通信首选 channel,共享状态用锁或单 goroutine 管理。

以上是本人在学习过程中遇到的困难和疑惑,希望这些分享可以帮到你,也希望得到你的指导和建议。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/24 15:10:09

Open-AutoGLM运行异常?:5步精准定位并解决核心故障

第一章&#xff1a;Open-AutoGLM 故障排查指南在部署和使用 Open-AutoGLM 框架过程中&#xff0c;可能会遇到模型加载失败、推理超时或 API 调用异常等问题。本章提供常见故障的诊断路径与解决方案&#xff0c;帮助开发者快速恢复服务。环境依赖检查 确保运行环境满足最低依赖要…

作者头像 李华
网站建设 2026/2/19 2:22:28

Open-AutoGLM性能瓶颈深度剖析(专家级调优方案限时公开)

第一章&#xff1a;Open-AutoGLM性能瓶颈概述 Open-AutoGLM作为一款基于开源架构的自动化通用语言模型系统&#xff0c;在实际部署与高并发场景下暴露出若干关键性能瓶颈。这些问题主要集中在推理延迟、内存占用和并行处理效率三个方面&#xff0c;直接影响系统的响应速度与可扩…

作者头像 李华
网站建设 2026/2/24 2:51:35

Jupyter是什么?如何安装使用?

What&#xff5c;Jupyter 到底是什么&#xff1f; &#x1f4d3; 一套「交互式计算」开源生态&#xff0c;核心产品 Jupyter Notebook&#xff1a;把代码、运行结果、公式、图表、Markdown 说明整合在一个网页文件&#xff08;.ipynb&#xff09;里&#xff0c;边写边跑边看图&…

作者头像 李华
网站建设 2026/2/23 8:05:20

Open-AutoGLM部署实战全记录(从零到上线的完整路径)

第一章&#xff1a;Open-AutoGLM部署实战全记录&#xff08;从零到上线的完整路径&#xff09;在企业级AI应用落地过程中&#xff0c;Open-AutoGLM作为一款开源的自动化生成语言模型框架&#xff0c;提供了高效的推理与微调能力。本章将详细记录从环境准备到服务上线的完整部署…

作者头像 李华
网站建设 2026/2/20 10:01:33

14.2 全流程拆解:每个环节的关键任务与交付物

14.2 全流程拆解:每个环节的关键任务与交付物 在上一节中,我们介绍了AIGC产品的标准设计流程。今天,我们将对这个流程进行详细拆解,深入分析每个环节的关键任务和预期交付物,帮助大家更好地理解和执行AIGC产品开发项目。 需求分析与机会识别阶段 这个阶段是整个产品开发…

作者头像 李华
网站建设 2026/2/22 3:48:16

仅限内部流传的优化技巧:Open-AutoGLM缩放手势识别三大禁忌与破解

第一章&#xff1a;Open-AutoGLM缩放手势识别优化概述Open-AutoGLM 是一种基于自回归语言模型与视觉编码协同的多模态交互框架&#xff0c;其在移动设备端的手势识别任务中展现出卓越的实时性与准确性。针对缩放手势&#xff08;Pinch-to-Zoom&#xff09;这一高频交互行为&…

作者头像 李华