Golang 协程可以无限创建吗?

Go 语言目前很火热,一部分原因在于自身带"高并发"的标签,其本身就拥有及其优秀的并发量和吞吐量。

1 协程可以无限创建吗?

我们在日常开发中会有高并发场景,有时会用多协程并发实现。在高并发业务场景,能否可以随意开辟 goroutine 并且放养不管呢?毕竟有强大的 GC 和优越的 GMP 调度算法。

看下面的代码:

go 复制代码
package main

import (
    "fmt"
    "math"
    "runtime"
)

func main() {
    taskCount := math.MaxInt64

    for i := 0; i < taskCount; i++ {
        go func(i int) {
            fmt.Println("go func ", i, " goroutine count = ", runtime.NumGoroutine())
        }(i)
    }
}

运行结果:

结果可以看到,程序最终会被系统强制 kill 掉,强制结束进程。

如果我们大量的开启 goroutine 会占满某一时间操作系统上用户态程序共享的资源,其中包括 CPU、Memory、Fd 等。从而导致系统瘫痪甚至影响其他程序。

  • CPU 使用率瞬间上涨
  • Memory 占用不断上涨
  • 主进程崩溃,强制 Kill

所以我们开发中一定要重视。

2 如何控制 goroutine 数量

2.1 通过 buffer channl 来控制 goroutine

go 复制代码
package main

import (
    "fmt"
    "runtime"
)

func work(ch chan bool, i int) {
    fmt.Println("go func ", i, " goroutine count = ", runtime.NumGoroutine())
    <-ch
}

func main() {
    taskCount := 10
    
    ch := make(chan bool, 3)
    for i := 0; i < taskCount; i++ {
        ch <- true
        go work(ch, i)
    }
}

程序运行结果:

解读下代码,这里我们用了 3个 channel 对应 3 个 goroutine 执行任务。在同一时间内运行的 goroutine 的数量与 channel 限制 buffer 的数量是一致的,从而达到限制 goroutine 的效果。

2.2 通过 sync.WaitGroup 来控制 goroutine

go 复制代码
package main

import (
    "fmt"
    "math"
    "sync"
    "runtime"
)

var wg = sync.WaitGroup{}

func work(i int) {
    fmt.Println("go func ", i, " goroutine count = ", runtime.NumGoroutine())
    wg.Done()
}

func main() {
    //模拟用户需求业务的数量
    taskCount := math.MaxInt64
    for i := 0; i < taskCount; i++ {
		wg.Add(1)
        go work(i)
    }
	wg.Wait()
}

运行结果:

从运行结果可以看出,进程还是被操作系统强制 Kill 了,使用 sync.WaitGroup{} 并不能控制 goroutine 的数量。

2.3 channel & sync.WaitGroup 同步组合方式

go 复制代码
package main

import (
    "fmt"
    "math"
    "sync"
    "runtime"
)

var wg = sync.WaitGroup{}

func work(ch chan bool, i int) {
    fmt.Println("go func ", i, " goroutine count = ", runtime.NumGoroutine())
    <-ch

    wg.Done()
}

func main() {
    //模拟用户需求go业务的数量
    taskCount := math.MaxInt64

    ch := make(chan bool, 3)

    for i := 0; i < taskCount; i++ {
		wg.Add(1)
        ch <- true
        go work(ch, i)
    }

	  wg.Wait()
}

运行结果:

进程没有被操作系统 Kill,通过 buffer channel 这种控制住了 goroutine 数量。

2.4 无 buffer channel 控制 goroutine 数量

go 复制代码
package main

import (
    "fmt"
    "sync"
    "runtime"
)
var wg = sync.WaitGroup{}

func work(ch chan int) {
    for i := range ch {
        fmt.Println("go func ", i, " goroutine count = ", runtime.NumGoroutine())
        wg.Done()
    }
}

func sendTask(task int, ch chan int) {
    wg.Add(1)
    ch <- task
}

func main() {
    // 无 buffer channel
    ch := make(chan int)   

    goCount := 3              
    for i := 0; i < goCount; i++ {
        // 启动go
        go busi(ch)
    }

    taskCount := 10 
    for t := 0; t < taskCount; t++ {
        // 发送任务
        sendTask(t, ch)
    }

	wg.Wait()
}

运行结果:

首先创建了无 buffer 的 channel,将任务发送到 channel 中,通过控制 goroutine 数量的方式执行程序,达到控制 goroutine。

2.5 协程池方式控制 goroutine

线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。使用线程池避免了在处理短时间任务时创建和销毁线程的代价。

字节跳动:

github.com/bytedance/g...

相关推荐
苏渡苇6 分钟前
从零搭建 Vue 3 工程化项目:Vite + TypeScript + Pinia + Element Plus 实战
前端·vue.js·typescript
CallFay云起未来10 分钟前
2026年AI客服能不能减少人工回复?从知识检索到人机协同的技术实现
java·大数据·前端·人工智能·架构·文心一言
IT_陈寒12 分钟前
一文搞懂SpringBoot的核心概念
前端·人工智能·后端
棉猴24 分钟前
玩游戏学Python12-布尔型与条件语句
java·前端·python·玩游戏·游戏编程·pgzero
java_nnnn27 分钟前
Spring Web MVC 入门:从一个 URL 到 Java 后端完整处理请求
java·前端·spring boot·spring
丨白色风车丨39 分钟前
LangChain Agent 从入门到实战:创建与调用参数全解析
前端·python·langchain
Jmyd01239 小时前
Web端3D营销展厅技术实现路径:从素材上云到线索回流的数据链路
前端·3d·性能优化·三维数字化
砚凝霜9 小时前
【软考信息安全】第二十二章 Web服务器安全分析与增强——Apache与IIS
服务器·前端·安全
u01110267511 小时前
JPG、PNG、WebP 怎么选?按透明度、体积和使用场景做判断
前端·javascript·vue.js·图像处理·ai作画
jerrywus11 小时前
让 AI 写完代码后交一份报告:我改造了 answer-me-with-html
前端·claude·cursor