goroutines导致严重的减速和头痛问题

huangapple go评论85阅读模式
英文:

goroutines causing major slowdowns and headaches

问题

我在使用goroutines时遇到了一些问题。为什么这段代码在顺序执行时需要大约125毫秒才能执行完:

package main

import (
  "os/exec"
  "time"
  "fmt"
)

func main() {
  cmd := exec.Command("lessc", "--yui-compress", "test.less")
  n := 2000
  start := time.Now()
  for i := 0; i < n; i++ {
    cmd.Run()
  }
  finish := time.Now()

  fmt.Printf("程序运行时间为 %v\n", finish.Sub(start))
}

而当使用goroutines进行并发执行时,这段代码需要大约20秒:

package main

import (
  "os/exec"
  "time"
  "fmt"
)

func main() {
  cmd := exec.Command("lessc", "--yui-compress", "test.less")
  ch := make(chan bool)
  n := 2000
  start := time.Now()
  for i := 0; i < n; i++ {
    go lessc(ch, cmd)
  }
  fmt.Println(n, "个goroutines已启动。")
  for i := 0; i < n; i++ {
    _ = <-ch
  }
  finish := time.Now()

  fmt.Printf("程序运行时间为 %v\n", finish.Sub(start))
}

func lessc(ch chan bool, c *exec.Cmd) {

  c.Run()
  ch <- true
}

在i7 720QM (4C/8T) 8GB RAM linux/x86-64上使用go 1.0.3进行编译和测试,也在相同的机器上使用1.0.2进行了构建和测试,结果出现了相同的问题。

编辑:由@jnml解决。如果有人关心新的修复并发代码,这里是:

package main

import (
  "os/exec"
  "time"
  "fmt"
)

func main() {
  ch := make(chan bool)
  n := 2000
  start := time.Now()
  for i := 0; i < n; i++ {
    go lessc(ch)
  }
  fmt.Println(n, "个goroutines已启动。")
  for i := 0; i < n; i++ {
    _ = <-ch
  }
  finish := time.Now()

  fmt.Printf("程序运行时间为 %v\n", finish.Sub(start))
}

func lessc(ch chan bool) {

  cmd := exec.Command("lessc", "--yui-compress", "test.less")
  cmd.Run()
  ch <- true
}
英文:

I'm having something of a problem with goroutines. Why is it that this code executes in ~125ms (note sequential execution):

package main

import (
  &quot;os/exec&quot;
  &quot;time&quot;
  &quot;fmt&quot;
)

func main() {
  cmd := exec.Command(&quot;lessc&quot;, &quot;--yui-compress&quot;, &quot;test.less&quot;)
  n := 2000
  start := time.Now()
  for i := 0; i &lt; n; i++ {
    cmd.Run()
  }
  finish := time.Now()

  fmt.Printf(&quot;Program took %v to run\n&quot;, finish.Sub(start))
}

When this code takes about 20 seconds (concurrent execution using goroutines):

package main

import (
  &quot;os/exec&quot;
  &quot;time&quot;
  &quot;fmt&quot;
)

func main() {
  cmd := exec.Command(&quot;lessc&quot;, &quot;--yui-compress&quot;, &quot;test.less&quot;)
  ch := make(chan bool)
  n := 2000
  start := time.Now()
  for i := 0; i &lt; n; i++ {
    go lessc(ch, cmd)
  }
  fmt.Println(n, &quot; goroutines started.&quot;)
  for i := 0; i &lt; n; i++ {
    _ = &lt;-ch
  }
  finish := time.Now()

  fmt.Printf(&quot;Program took %v to run\n&quot;, finish.Sub(start))
}

func lessc(ch chan bool, c *exec.Cmd) {

  c.Run()
  ch &lt;- true
}

Using go 1.0.3 on i7 720QM (4C/8T) 8GB RAM linux/x86-64
Also built and tested using 1.0.2 and got the same problem on the same machine.

Edit: Solved by @jnml below. If anyone cares about the new fixed concurrent code here it is:

package main

import (
  &quot;os/exec&quot;
  &quot;time&quot;
  &quot;fmt&quot;
)

func main() {
  ch := make(chan bool)
  n := 2000
  start := time.Now()
  for i := 0; i &lt; n; i++ {
    go lessc(ch)
  }
  fmt.Println(n, &quot; goroutines started.&quot;)
  for i := 0; i &lt; n; i++ {
    _ = &lt;-ch
  }
  finish := time.Now()

  fmt.Printf(&quot;Program took %v to run\n&quot;, finish.Sub(start))
}

func lessc(ch chan bool) {

  cmd := exec.Command(&quot;lessc&quot;, &quot;--yui-compress&quot;, &quot;test.less&quot;)
  cmd.Run()
  ch &lt;- true
}

答案1

得分: 11

我认为你的程序不正确。它包含了竞态条件,因此可以做任何事情。它的任何时间都没有意义。

你创建了一个exec.Cmd,然后并发(==数据竞争)从多个goroutine中执行它的Run方法。exec.Cmd从未提到它可以被重用来运行多次 - 即使是串行地。

exec.Cmdexec.Command初始化了一些状态,并在执行Run后有不同的状态。也就是说,在执行Run方法后,状态不再初始化,可能不适合另一个Run

英文:

IMO your program is not correct. It contains a race condition and thus can do literally anything. Any timing of it don't make sense.

You're creating one exec.Cmd and then concurrently (== data race) perform its Run method from several goroutines. exec.Cmd never mentions it could be reused for more than once Run - even if serially.

exec.Cmd has some state initialized by exec.Command and a different state after executing Run. IOW, after executing the Run method, the state is not anymore initialized and probably not fit for another Run

huangapple
  • 本文由 发表于 2013年4月17日 18:20:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/16057524.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定