代码之家  ›  专栏  ›  技术社区  ›  kmad1729

goroutine阻塞和非阻塞使用

  •  0
  • kmad1729  · 技术社区  · 8 年前

    我正在努力理解日常工作是如何进行的。以下是一些代码:

    //parallelSum.go
    func sum(a []int, c chan<- int, func_id string) {
        sum := 0
        for _, n := range a {
            sum += n
        }
        log.Printf("func_id %v is DONE!", func_id)
        c <- sum
    }   
    func main() {
        ELEM_COUNT := 10000000
        test_arr := make([]int, ELEM_COUNT)
        for i := 0; i < ELEM_COUNT; i++ {
            test_arr[i] = i * 2 
        }
        c1 := make(chan int)
        c2 := make(chan int)
        go sum(test_arr[:len(test_arr)/2], c1, "1")
        go sum(test_arr[len(test_arr)/2:], c2, "2")
        x := <-c1
        y := <-c2
    
        //x, y := <-c, <-c
        log.Printf("x= %v, y = %v, sum = %v", x, y, x+y)
    }   
    

    上述程序运行正常并返回输出。我有一个相同程序的迭代版本:

    //iterSum.go
    func sumIter(a []int, c *int, func_id string) {
        sum := 0
        log.Printf("entered the func %s", func_id)
        for _, n := range a { 
            sum += n
        }   
        log.Printf("func_id %v is DONE!", func_id)
        *c = sum 
    }
    func main() {
        */
        ELEM_COUNT := 10000000
        test_arr := make([]int, ELEM_COUNT)
        for i := 0; i < ELEM_COUNT; i++ {
            test_arr[i] = i * 2
        }
        var (
            i1 int
            i2 int
        )   
        sumIter(test_arr[:len(test_arr)/2], &i1, "1")
        sumIter(test_arr[len(test_arr)/2:], &i2, "2")
        x := i1
        y := i2
    
        log.Printf("x= %v, y = %v, sum = %v", x, y, x+y)
    } 
    

    我运行该程序20次,并平均每个程序的运行时间。我看到平均值几乎相等?并行化不应该让事情变得更快吗?我做错了什么?

    下面是运行20次的python程序:

    iterCmd = 'go run iterSum.go'
    parallelCmd = 'go run parallelSum.go'
    
    runCount = 20
    
    
    def analyzeCmd(cmd, runCount):
        runData = []
        print("running cmd (%s) for (%s) times" % (cmd, runCount))
        for i in range(runCount):
        ┆   start_time = time.time()
        ┆   cmd_out = subprocess.check_call(shlex.split(cmd))
            run_time = time.time() - start_time
        ┆   curr_data = {'iteration': i, 'run_time' : run_time}
        ┆   runData.append(curr_data)
    
        return runData
    
    iterOut = analyzeCmd(iterCmd, runCount)
    parallelOut = analyzeCmd(parallelCmd, runCount)
    
    print("iter cmd data -->")
    print(iterOut)
    
    with open('iterResults.json', 'w') as f:
        json.dump(iterOut, f)
    
    print("parallel cmd data -->")
    print(parallelOut)
    
    with open('parallelResults.json', 'w') as f:
        json.dump(parallelOut, f)
    
    avg = lambda results: sum(i['run_time'] for i in results) / len(results)
    print("average time for iterSum = %3.2f" % (avg(iterOut)))
    print("average time for parallelSum = %3.2f" % (avg(parallelOut)))
    

    以下是1次运行的输出:

    average time for iterSum = 0.27
    average time for parallelSum = 0.29
    
    1 回复  |  直到 8 年前
        1
  •  2
  •   Kaedys    8 年前

    所以,这里有几个问题。首先,在并发示例中,您的通道没有被缓冲,这意味着接收端可能仍然需要彼此等待一段时间。第二 同时发生的 并不意味着 平行的 . 您确定这些线程实际上是并行运行的,而不是简单地调度在同一个OS线程上吗?

    也就是说,您这里的主要问题是Python代码正在使用 go run 对于每个迭代,这意味着您记录的绝大多数“运行时”实际上是 汇编 您的代码的( 开始跑步 编译并运行指定的文件,而它在设计上不会缓存任何文件)。如果要测试运行时,请使用 Go's benchmark system ,而不是您自己的拼凑版本。你会得到更准确的结果。例如,除了编译瓶颈之外,也无法确定Python代码本身引入了多少瓶颈。

    哦,您应该摆脱使用函数引用参数作为“返回”值的方式的习惯。Go支持多个返回,所以C风格的就地修改参数通常被认为是一种反模式,除非有真正令人信服的理由这样做。