Golang中频繁启动Goroutine会对机器造成性能负担吗
Golang中频繁启动Goroutine会对机器造成性能负担吗 你好
你是否为不同的"数据库查询"使用 Goroutine? 我在"数据库查询"上尝试过,我的意思是一个 Goroutine 用于"更新",一个用于"插入"等等… 这比"简单函数"要慢 那么在下面的代码中,为什么"简单函数"比"Goroutines 函数"更快?
这里是 playGround:
代码审查:
package main
import (
p "fmt"
w "sync"
t "time"
)
var wg w.WaitGroup
func main() {
simple(200)
goRoutines(200)
}
简单函数:
func simple(n int) {
startTime := t.Now().UnixNano()
for i:=0;i< n;i++ {
gen(10, 50, 100, 200)
}
p.Println("simple : ",t.Now().UnixNano()-startTime)
}
func gen(n ...int) {
var slice []int
for _, k := range n {
slice = append(slice, k*10+50/2)
}
}
Goroutines 函数:
func goRoutines(n int) {
startTime := t.Now().UnixNano()
wg.Add(n)
for i:=0;i< n;i++ {
go gen2(10, 50, 100, 200)
}
wg.Wait()
p.Println("goRoutines : ",t.Now().UnixNano()-startTime)
}
func gen2(n ...int) {
var slice []int
for _, k := range n {
slice = append(slice, k*10+50/2)
}
wg.Done()
}
结果:
simple : 49033794
goRoutines : 65791173
提前感谢。
更多关于Golang中频繁启动Goroutine会对机器造成性能负担吗的实战教程也可以访问 https://www.itying.com/category-94-b0.html
感谢,Johan
链接已失效(在此阅读更多内容)
奇怪。点击这里的链接不起作用,但我在谷歌上搜索后却能进入该页面,而且地址仍然相同 😕
试试这个:https://www.google.se/search?q=Why+goroutines+are+not+lightweight+threads
这里还有一些链接:http://www.golangbootcamp.com/book/concurrency 特别是Rob Pike关于"并发不是并行"的演讲非常有趣。
你的 Go 协程版本运行较慢的原因是:协程会一直运行直到遇到阻塞调用,比如等待磁盘、网络等操作。因此,在紧密循环中运行协程不会给其他协程分配时间,除非你拥有多个核心,这样它们才能并行执行。
更多信息请阅读:https://codeburst.io/why-goroutines-are-not-lightweight-threads-7c460c1f155
不要中断!
Go 运行时调度器采用协作式调度,这意味着只有当当前协程阻塞或完成时,才会调度另一个协程。以下是一些情况:
- 通道的发送和接收操作(如果这些操作会阻塞)
- Go 语句(尽管不能保证新协程会立即被调度)
- 阻塞性系统调用(如文件和网络操作)
- 在垃圾回收周期停止后
在Golang中,频繁启动Goroutine本身不会直接对机器造成显著的性能负担,因为Goroutine是轻量级的,由Go运行时管理。然而,在你的代码中,"简单函数"比"Goroutines函数"更快的原因在于任务的性质和Goroutine的开销。
你的gen和gen2函数执行的是非常简单的计算(仅涉及整数运算和切片追加),这种操作在单个Goroutine中顺序执行时,CPU可以高效处理,几乎没有上下文切换的开销。而使用Goroutines时,尽管它们是轻量的,但每个Goroutine的创建、调度和同步(通过WaitGroup)都会引入额外的开销,包括:
- Goroutine的初始化和调度成本。
WaitGroup的原子操作(如Add、Done和Wait)导致的锁竞争和内存屏障。- 在多个Goroutines间切换的上下文开销。
对于这种计算密集型但简单的任务,Goroutines的并行优势无法抵消这些开销,因此顺序执行更快。只有当任务涉及I/O操作(如数据库查询、网络请求)或复杂计算时,Goroutines才能通过并发提高性能,因为它们允许在等待时执行其他任务。
在你的代码中,gen2函数没有实际I/O,只是纯计算,因此Goroutines版本更慢。以下是代码示例,展示了一个更平衡的用例,其中Goroutines可能更快(例如,模拟I/O延迟):
package main
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
func main() {
simple(200)
goRoutines(200)
}
func simple(n int) {
startTime := time.Now().UnixNano()
for i := 0; i < n; i++ {
gen(10, 50, 100, 200)
}
fmt.Println("simple : ", time.Now().UnixNano()-startTime)
}
func gen(n ...int) {
var slice []int
for _, k := range n {
slice = append(slice, k*10+50/2)
}
// 模拟轻微延迟,以接近真实场景
time.Sleep(1 * time.Microsecond)
}
func goRoutines(n int) {
startTime := time.Now().UnixNano()
wg.Add(n)
for i := 0; i < n; i++ {
go gen2(10, 50, 100, 200)
}
wg.Wait()
fmt.Println("goRoutines : ", time.Now().UnixNano()-startTime)
}
func gen2(n ...int) {
var slice []int
for _, k := range n {
slice = append(slice, k*10+50/2)
}
// 模拟轻微延迟
time.Sleep(1 * time.Microsecond)
wg.Done()
}
在这个修改版本中,如果添加了time.Sleep模拟I/O延迟,Goroutines版本可能更快,因为它能并发处理等待。但在原代码中,由于任务太简单,顺序执行总是更高效。总结:Goroutines适用于I/O密集型或可并行化的重型计算任务,而非简单循环计算。


