频道与并行混淆

时间:2018-02-23 09:10:31

标签: go

我正在学习Golang,我对并行性以及它如何在Golang中实现感到困惑。

给出以下示例:

package main

import (
    "fmt"
    "sync"
    "math/rand"
    "time"
)


const (
    workers = 1
    rand_count = 5000000
)


func start_rand(ch chan int) {
    defer close(ch)
    var wg sync.WaitGroup
    wg.Add(workers)
    rand_routine := func(counter int) {
        defer wg.Done()
        for i:=0;i<counter;i++ {
            seed := time.Now().UnixNano()
            rand.Seed(seed)
            ch<-rand.Intn(5000)

        }
    }
    for i:=0; i<workers; i++ {
        go rand_routine(rand_count/workers)
    }
    wg.Wait()
}

func main() {
    start_time := time.Now()
    mychan := make(chan int, workers)
    go start_rand(mychan)
    var wg sync.WaitGroup
    wg.Add(workers)

    work_handler := func() {
        defer wg.Done()
        for {
            v, isOpen := <-mychan
            if !isOpen { break }
            fmt.Println(v)
        }
    }
    for i:=0;i<workers;i++ {
        go work_handler()
    }
    wg.Wait()
    elapsed_time := time.Since(start_time)
    fmt.Println("Done",elapsed_time)
}

这段代码大约需要一分钟才能在我的Macbook上运行。我认为增加“工人”常量会启动额外的例程,并且由于我的笔记本电脑有多个核心,因此会缩短执行时间。

但事实并非如此。增加工作人员不会减少执行时间。

我在考虑将工人设置为1,将创建1个goroutine来生成随机数,并将其设置为4,将创建4个goroutines。考虑到我的笔记本电脑的多核特性,我预计4名工作人员将在不同的核心上运行,因此,提高了性能。 但是,我看到所有内核的负载增加,即使工作人员设置为1.我在这里缺少什么?

1 个答案:

答案 0 :(得分:1)

您的代码存在一些问题,导致其内在缓慢:

  • 你正在循环中播种。这只需要做一次
  • 您使用相同的随机数源。此源是线程安全的,但会消除并发工作程序的任何性能提升。您可以使用rand.New
  • 为每个工作人员创建一个来源
  • 你正在打印很多东西。打印也是线程安全的。这样就可以减少并发工人的速度。
  • 正如Zak已经指出的那样:go例程中的并发工作非常便宜而且通信费用很高。

您可以像这样重写您的程序。然后,当您更改工人数量时,您将看到一些速度提升:

package main

import (
    "fmt"
    "math/rand"
    "time"
)

const (
    workers   = 1
    randCount = 5000000
)

var results = [randCount]int{}

func randRoutine(start, counter int, c chan bool) {
    r := rand.New(rand.NewSource(time.Now().UnixNano()))
    for i := 0; i < counter; i++ {
        results[start+i] = r.Intn(5000)
    }
    c <- true
}

func main() {
    startTime := time.Now()
    c := make(chan bool)

    start := 0
    for w := 0; w < workers; w++ {
        go randRoutine(start, randCount/workers, c)
        start += randCount / workers
    }

    for i := 0; i < workers; i++ {
        <-c
    }

    elapsedTime := time.Since(startTime)
    for _, i := range results {
        fmt.Println(i)
    }
    fmt.Println("Time calulating", elapsedTime)

    elapsedTime = time.Since(startTime)
    fmt.Println("Toal time", elapsedTime)
}

这个程序在日常工作中做了很多工作,并且沟通很少。每个例行程序也使用不同的随机源。