2016-10-30 2 views
6

「funcリテラル」が何であるかわからないので、このエラーはちょっと混乱しています。私は問題を見ると思う - 私は新しい行くルーチンの中から範囲値の変数を参照しているので、値はいつでも変更され、私たちが期待するものではないかもしれません。問題を解決する最良の方法は何ですか?問題のそれぞれのループの中でgoルーチンを使用するときにfuncリテラルによって捕捉されたvet range変数

コード:

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 
} 

私の提案の修正:

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 
} 

しかし、実際に問題を解決しないこと?私はちょうど範囲変数から値が別のローカル変数に移動しました。その値は、私が入っている各ループについての反復に基づいています。

答えて

5

それが行くで新規参入のためのよくある間違いだ、とはいVAR currentProcessは、各ループの変更、ので、あなたのゴルーチンはすべて、スライス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 
} 
+2

すばらしい見た目の更新されたコードをありがとう!私は非常に似たようなものに関連して修正するのに1時間以上かかったコードにぎこちないバグがありました。私はスライスが既にポインタであることを認識しないうちに、LoaderProcessを返していたので、各ポインタが最後のコマンドである同じLoaderProcessインスタンスを指していたポインタのスライスを本質的に戻していました。実行後毎回異なる。だから、1つのコードから2つの大きな教訓が得られました。ありがとうございました。 – anon58192932

1

はい、あなたがしたのは、この警告は適切です。

修正の前には、という単一のという変数しかなく、すべてのゴルーチンがそれを参照していました。つまり、開始時点の値は表示されず、現在の値が表示されます。ほとんどの場合、これは範囲の最後のものです。

+0

ありがとうございます。 funcリテラルがインラインで定義された関数であることを確認できますか?インライン・ルー・ルーチンで必要とされるようなもの? – anon58192932

関連する問題