我不太确定'func literal'是什么因此这个错误让我有点困惑.我想我看到了这个问题 - 我在一个新的go例程中引用了一个范围值变量,因此值可能随时改变,而不是我们所期望的.什么是解决问题的最佳方法?
有问题的代码:
func (l *Loader) StartAsynchronous() []LoaderProcess {
for _, currentProcess := range l.processes {
cmd := exec.Command(currentProcess.Command, currentProcess.Arguments...)
log.LogMessage("Asynchronously executing LoaderProcess: %+v", currentProcess)
go func() {
output, err := cmd.CombinedOutput()
if err != nil {
log.LogMessage("LoaderProcess exited with error status: %+v\n %v", currentProcess, err.Error())
} else {
log.LogMessage("LoaderProcess exited successfully: %+v", currentProcess)
currentProcess.Log.LogMessage(string(output))
}
time.Sleep(time.Second * TIME_BETWEEN_SUCCESSIVE_ITERATIONS)
}()
}
return l.processes
}
Run Code Online (Sandbox Code Playgroud)
我建议修复:
func (l *Loader) StartAsynchronous() []LoaderProcess {
for _, currentProcess := range l.processes {
cmd := exec.Command(currentProcess.Command, currentProcess.Arguments...)
log.LogMessage("Asynchronously executing LoaderProcess: %+v", currentProcess)
localProcess := currentProcess
go func() {
output, err := cmd.CombinedOutput()
if err != nil {
log.LogMessage("LoaderProcess exited with error status: %+v\n %v", localProcess, err.Error())
} else {
log.LogMessage("LoaderProcess exited successfully: %+v", localProcess)
localProcess.Log.LogMessage(string(output))
}
time.Sleep(time.Second * TIME_BETWEEN_SUCCESSIVE_ITERATIONS)
}()
}
return l.processes
}
Run Code Online (Sandbox Code Playgroud)
但这真的解决了这个问题吗?我刚刚将范围变量的引用移动到另一个局部变量,该变量的值基于我所在的每个循环的迭代.
Yan*_*ozo 33
不要感觉不好这是Go中新来者的常见错误,是的,每个循环的var currentProcess都会发生变化,所以你的goroutines将使用切片l.processes中的最后一个进程,所有你需要做的就是将变量传递给匿名函数的参数,如下所示:
func (l *Loader) StartAsynchronous() []LoaderProcess {
for ix := range l.processes {
go func(currentProcess *LoaderProcess) {
cmd := exec.Command(currentProcess.Command, currentProcess.Arguments...)
log.LogMessage("Asynchronously executing LoaderProcess: %+v", currentProcess)
output, err := cmd.CombinedOutput()
if err != nil {
log.LogMessage("LoaderProcess exited with error status: %+v\n %v", currentProcess, err.Error())
} else {
log.LogMessage("LoaderProcess exited successfully: %+v", currentProcess)
currentProcess.Log.LogMessage(string(output))
}
time.Sleep(time.Second * TIME_BETWEEN_SUCCESSIVE_ITERATIONS)
}(&l.processes[ix]) // passing the current process using index
}
return l.processes
}
Run Code Online (Sandbox Code Playgroud)
Jai*_*ano 24
对于那些寻找更简单示例的人:
这是错误的:
func main() {
for i:=0; i<10; i++{
go func(){
// Here i is a "free" variable, since it wasn't declared
// as an explicit parameter of the func literal,
// so IT'S NOT copied by value as one may infer. Instead,
// the "current" value of i
// (in most cases the last value of the loop) is used
// in all the go routines once they are executed.
processValue(i)
}()
}
}
func processValue(i int){
fmt.Println(i)
}
Run Code Online (Sandbox Code Playgroud)
不完全是错误,但可能导致意外行为,因为控制循环的变量i可能会从其他 go 例程中更改。它实际上是发出警告的go vet 命令。Go vet 有助于精确地找到这种可疑的结构,它使用启发式方法不能保证所有报告都是真正的问题,但它可以找到编译器没有发现的错误。所以不时运行它是一个很好的做法。
Go Playground在运行代码之前先运行go vet,你可以在这里看到它的作用。
这是对的:
func main() {
for i:=0; i<10; i++{
go func(differentI int){
processValue(differentI)
}(i) // Here i is effectively passed by value since it was
// declared as an explicit parameter of the func literal
// and is taken as a different "differentI" for each
// go routine, no matter when the go routine is executed
// and independently of the current value of i.
}
}
func processValue(i int){
fmt.Println(i)
}
Run Code Online (Sandbox Code Playgroud)
我故意将 func 文字参数命名为differentI以使其明显是一个不同的变量。这样做对于并发使用是安全的,go vet不会抱怨并且你不会得到任何奇怪的行为。你可以在这里看到它的实际效果。(因为打印是在不同的 go 例程上完成的,所以你什么也看不到,但程序会成功退出)
顺便说一句,func文字基本上是一个匿名函数:)
是的,您所做的是正确修复此警告的最简单方法。
在修复之前,只有一个变量,所有 goroutine 都引用它。这意味着他们看到的不是开始时的值,而是当前的值。在大多数情况下,这是该系列中的最后一个。
| 归档时间: |
|
| 查看次数: |
8666 次 |
| 最近记录: |