goroutine阻止和非阻塞使用

时间:2018-01-30 21:13:24

标签: go channel goroutine

我试图了解常规程序是如何工作的。这是一些代码:

//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 个答案:

答案 0 :(得分:2)

所以,这里有几个问题。首先,您的通道不会在并发示例中进行缓冲,这意味着接收仍然可能需要彼此等待一段时间。其次,并发并不意味着 parallel 。你确定它们实际上并行运行而不是简单地安排在同一个OS线程上吗?

那就是说,你的主要问题是你的Python代码每次迭代使用go run,这意味着你记录的“运行时间”的绝大部分实际上是编译你的代码(go run编译然后运行指定的文件,它特别是设计不会缓存任何一个)。如果您想测试运行时,请使用Go's benchmark system,而不是您自己的拼凑版本。你会得到更准确的结果。例如,除了编译瓶颈之外,还无法确定Python代码本身引入了多少瓶颈。

哦,你应该养成使用函数的引用参数作为“返回”值的方法。 Go支持多个返回,因此原则上修改参数的C风格通常被认为是反模式,除非有一个非常有说服力的理由去做。