为啥使用goroutine后计算任务的总时间基本一样

Posted

技术标签:

【中文标题】为啥使用goroutine后计算任务的总时间基本一样【英文标题】:Why the total time of computing tasks after using goroutine is basically the same为什么使用goroutine后计算任务的总时间基本一样 【发布时间】:2022-01-21 23:26:16 【问题描述】:

不使用 goroutine 时,500 * 100000000 次加一

// 1m12.2857724s
    start := time.Now()
    for i := 0; i < 500; i++ 
        res := 0
        for j := 0; j < 100000000; j++ 
            res++
        
    
    duration := time.Since(start)
    fmt.Println(duration)

使用 goroutine 时,10 个 goroutine 执行 50 * 100000000 次加一

// 1m12.0174541s
    start := time.Now()
    ch := make(chan bool)
    for i := 0; i < 10; i++ 
        go func(ch chan bool) 
            for i := 0; i < 50; i++ 
                res := 0
                for j := 0; j < 100000000; j++ 
                    res++
                
            
            ch <- true
        (ch)
        <- ch
    

    duration := time.Since(start)
    fmt.Println(duration)

为什么使用 goroutine 不节省时间

【问题讨论】:

ch的目的是什么? 【参考方案1】:

ch 频道没有缓冲。你启动一个 goroutine 并在最后在通道上发送一个值,然后在启动另一个 goroutine 之前,你会从它接收。这是一个阻塞操作。在一个完成之前,你不会启动一个新的 goroutine。与第一个解决方案相比,您一无所获。

一个“解决方案”是让通道缓冲,并且只有在所有 goroutine 启动后才开始接收:

ch := make(chan bool, 10)
for i := 0; i < 10; i++ 
    go func(ch chan bool) 
        for i := 0; i < 50; i++ 
            res := 0
            for j := 0; j < 100000000; j++ 
                res++
            
        
        ch <- true
    (ch)


for i := 0; i < 10; i++ 
    <-ch

这将在我的计算机(4 个 CPU 内核)上带来几乎 4 倍的加速

等待所有 goroutine 的更好、更惯用的方法是使用 sync.WaitGroup

var wg sync.WaitGroup
for i := 0; i < 10; i++ 
    wg.Add(1)
    go func() 
        defer wg.Done()
        for i := 0; i < 50; i++ 
            res := 0
            for j := 0; j < 100000000; j++ 
                res++
            
        
    ()

wg.Wait()

另外请注意,使用多个 goroutine 仅在它们所做的任务“重要”时才值得,请参阅:

Matrix multiplication with goroutine drops performance

Vectorise a function taking advantage of concurrency

【讨论】:

【参考方案2】:

您可以有效地启动 10 个 goroutine,并在它们完成工作后从每个 goroutine 中以简单的方式接收。

检查你的代码的 CPU 使用率,以及这个

func add(ch chan int) 
    r := 0
    for i := 0; i < 50; i++ 
        for j := 0; j < 100000000; j++ 
            r++
        
    
    ch <- r


func main() 
    start := time.Now()
    ch := make(chan int)
    res := 0
    for i := 0; i < 10; i++ 
        go add(ch)
    
    for i := 0; i < 10; i++ 
        res += <-ch
    
    duration := time.Since(start)
    fmt.Println(duration, res)

输出

3.747405005s 50000000000

这将成功启动 10 个 goroutine,每个 goroutine 执行其工作并在完成后将结果返回到通道中。

【讨论】:

以上是关于为啥使用goroutine后计算任务的总时间基本一样的主要内容,如果未能解决你的问题,请参考以下文章

Go教程(十三)goroutine和channel

Go教程(十三)goroutine和channel

GO 计算所有并发任务的总时间 WaitGroup

GolangGoroutine原理

为啥在同一个 goroutine 中使用无缓冲通道会导致死锁?

golang-----golang sync.WaitGroup解决goroutine同步