英文:
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 (
"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("Program took %v to run\n", finish.Sub(start))
}
When this code takes about 20 seconds (concurrent execution using goroutines):
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 started.")
for i := 0; i < n; i++ {
_ = <-ch
}
finish := time.Now()
fmt.Printf("Program took %v to run\n", finish.Sub(start))
}
func lessc(ch chan bool, c *exec.Cmd) {
c.Run()
ch <- 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 (
"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 started.")
for i := 0; i < n; i++ {
_ = <-ch
}
finish := time.Now()
fmt.Printf("Program took %v to run\n", finish.Sub(start))
}
func lessc(ch chan bool) {
cmd := exec.Command("lessc", "--yui-compress", "test.less")
cmd.Run()
ch <- true
}
答案1
得分: 11
我认为你的程序不正确。它包含了竞态条件,因此可以做任何事情。它的任何时间都没有意义。
你创建了一个exec.Cmd
,然后并发(==数据竞争)从多个goroutine中执行它的Run
方法。exec.Cmd
从未提到它可以被重用来运行多次 - 即使是串行地。
exec.Cmd
在exec.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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论