以下のコードは、「予期しない実行」というコンパイルエラーを示します。
x := go doSomething(arg)
func doSomething(arg int) int{
...
return my_int_value
}
goroutineを使用せずに、関数を正常に呼び出すと、戻り値をフェッチできることはわかっています。または、チャンネルなどを使用できます。
私の質問は、なぜこのような戻り値をゴルーチンからフェッチできないのかということです。
以下のコードは、「予期しない実行」というコンパイルエラーを示します。
x := go doSomething(arg)
func doSomething(arg int) int{
...
return my_int_value
}
goroutineを使用せずに、関数を正常に呼び出すと、戻り値をフェッチできることはわかっています。または、チャンネルなどを使用できます。
私の質問は、なぜこのような戻り値をゴルーチンからフェッチできないのかということです。
回答:
厳密な答えはあなたがそれをすることができるということです。それはおそらく良い考えではありません。これを行うコードは次のとおりです。
var x int
go func() {
x = doSomething()
}()
これにより、新しいゴルーチンが生成され、計算されdoSomething()
て結果がに割り当てられx
ます。問題はx
、元のゴルーチンからどのように使用するのかということです。競合状態にならないように、スポーンされたゴルーチンがそれで完了していることを確認することをお勧めします。しかし、それを実行したい場合は、ゴルーチンと通信する方法が必要になります。それを実行する方法がある場合は、それを使用して値を送り返してみませんか?
return
これは、assign
MENT
goroutineを(非同期で)実行し、関数から戻り値をフェッチすることは、本質的に矛盾したアクションです。あなたが言うときgo
、あなたが意味する「非同期的にそれを行う」か、さらに簡単:「DO!に行く完成する機能の実行を待ちません」。ただし、関数の戻り値を変数に割り当てると、変数内にこの値があることが期待されます。したがって、これを行うとx := go doSomething(arg)
、「続けて、関数を待たないでください!待ってください-待ってください!待ってください!x
次の行のvarで戻り値にアクセスできる必要があります!」
ゴルーチンから値をフェッチする最も自然な方法はチャネルです。チャネルは、同時実行するゴルーチンを接続するパイプです。あるゴルーチンからチャネルに値を送信し、それらの値を別のゴルーチンまたは同期関数で受け取ることができます。以下を使用して、並行性を壊さないゴルーチンから値を簡単に取得できますselect
。
func main() {
c1 := make(chan string)
c2 := make(chan string)
go func() {
time.Sleep(time.Second * 1)
c1 <- "one"
}()
go func() {
time.Sleep(time.Second * 2)
c2 <- "two"
}()
for i := 0; i < 2; i++ {
// Await both of these values
// simultaneously, printing each one as it arrives.
select {
case msg1 := <-c1:
fmt.Println("received", msg1)
case msg2 := <-c2:
fmt.Println("received", msg2)
}
}
}
この例は、Go ByExampleから抜粋したものです。
Goは、主にCSP理論に基づいています。上記の素朴な説明は、CSPの観点から正確に概説できます(ただし、質問の範囲外だと思います)。少なくともRADであるため、CSP理論に精通することを強くお勧めします。これらの短い引用は、考え方の方向性を示しています。
その名前が示すように、CSPでは、独立して動作し、メッセージパッシング通信のみを介して相互作用するコンポーネントプロセスの観点からシステムを記述できます。
コンピュータサイエンスでは、メッセージパッシングはメッセージをプロセスに送信し、プロセスとサポートインフラストラクチャに依存して、実行する実際のコードを選択して呼び出します。メッセージパッシングは、プロセス、サブルーチン、または関数が名前で直接呼び出される従来のプログラミングとは異なります。
go
キーワードの考え方は、doSomething関数を非同期で実行し、結果を待たずに現在のゴルーチンを続行することです。これは、Bashシェルでコマンドの後に「&」を付けて実行するようなものです。あなたがしたい場合
x := doSomething(arg)
// Now do something with x
次に、doSomethingが終了するまでブロックする現在のゴルーチンが必要です。なぜだけではなく、doSomethingのを呼び出すことで、現在のゴルーチン?他のオプションもありますが(たとえば、doSomethingは、現在のゴルーチンが値を受け取るチャネルに結果を投稿できます)、doSomethingを呼び出して、結果を変数に割り当てる方が明らかに簡単です。
これは、Goクリエイターによるデザインの選択です。そこI / O操作非同期の値を表すための抽象化/ APIは全体の多くはだ- 、promise
、future
、async/await
、callback
、observable
等が挙げられる。これらの抽象化/ APIは、本質的にスケジューリングの単位に結びついている-コルーチン-及びこれらの抽象化/ APIをディクテーション方法コルーチン(より正確には、それらによって表される非同期I / Oの戻り値)を構成できます。
Goは、非同期I / O操作の戻り値を表すために、抽象化/ APIとしてメッセージパッシング(別名チャネル)を選択しました。そしてもちろん、ゴルーチンとチャネルは、非同期I / O操作を実装するための構成可能なツールを提供します。