http://play.golang.org/p/vhaKi5uVmm
package main
import "fmt"
var battle = make(chan string)
func warrior(name string, done chan struct{}) {
select {
case opponent := <-battle:
fmt.Printf("%s beat %s\n", name, opponent)
case battle <- name:
// I lost :-(
}
done <- struct{}{}
}
func main() {
done := make(chan struct{})
langs := []string{"Go", "C", "C++", "Java", "Perl", "Python"}
for _, l := range langs { go warrior(l, done) }
for _ = range langs { <-done }
}
[最初の質問]
done <- struct{}{}
この奇妙な構造がどのようにそしてなぜ必要なのでしょうか?空の構造体ですか、それとも匿名の構造体ですか?私はそれをグーグルで検索しましたが、これについて説明する正しい答えやドキュメントが見つかりませんでした。
元のソースはAndrew Gerrandの講演からです http://nf.wh3rd.net/10things/#1
ここに
make(chan struct{})
doneはstruct {}型のチャンネルです
だから私は試した
done <- struct{}
しかし、それは機能していません。この行に余分な括弧が必要なのはなぜですか?
done <- struct{}{}
[2番目の質問]
for _ = range langs { <-done }
なぜこの行が必要なのですか?この行がなければ、出力がないため、この行が必要であることを知っています。しかし、なぜこの行は何をするのでしょうか?そして、このコードで何が必要なのでしょうか?そんなこと知ってる <-done
は、完了したチャネルから値を受信し、受信した値を破棄します。しかし、なぜこれを行う必要があるのですか?
ありがとう!
複合リテラルは、構造体、配列、スライス、マップの値を構築し、評価されるたびに新しい値を作成します。これらは、複合要素の波括弧で区切られたリストが後に続く値のタイプで構成されます。要素は、単一の式またはキーと値のペアです。
struct{}{}
はstruct{}
型の複合リテラルであり、値の型に複合要素の波括弧で区切られたリストが続きます。
for _ = range langs { <-done }
は、すべてのlangs
のすべてのゴルーチンがdone
メッセージを送信するまで待機しています。
(intまたはboolではなく)チャネルにプッシュされる型にstruct {}を使用する興味深い側面の1つは、size空の構造体は... 0!
最近の記事「 The empty struct "(March 2014)by Dave Cheney 」を参照してください。
struct{}
好きなように(struct{}{}
)それらをあなたのチャンネルにプッシュします:あなたの記憶は影響を受けません。
ただし、「 Curious Channels 」に示すように、goルーチン間のシグナリングに使用できます。
そして、構造体にリンクされている他のすべての利点を保持します。
goでは、空の構造体を使用して、すべてのデータをグローバル変数に保存できます。すべての空の構造体は交換可能であるため、型のインスタンスは1つだけです。
たとえば、 empty struct errServerKeyExchange
が定義されているファイルの global var rsaKeyAgreement
を参照してください。
struct{}
は型(特に、メンバーのない構造体)です。タイプFoo
がある場合、Foo{field values, ...}
を使用して式にそのタイプの値を作成できます。これをまとめると、struct{}{}
はstruct{}
型の値であり、これはチャネルが期待するものです。
main
関数はwarrior
ゴルーチンを生成し、終了するとdone
チャネルに書き込みます。最後のfor
ブロックはこのチャネルから読み取り、main
がすべてのゴルーチンが終了するまで戻らないようにします。実行中の他のゴルーチンがあるかどうかに関係なく、main
が完了するとプログラムが終了するため、これは重要です。
良い質問、
このシナリオでの構造体チャネルの全体のポイントは、何か有用なことが起こったことを完了に知らせることです。チャネルの種類は実際には重要ではなく、intまたはboolを使用して同じ効果を達成できた可能性があります。重要なのは、彼のコードが同期化された方法で実行され、重要なポイントで信号を送り、先に進むために必要な簿記を行っていることです。
struct{}{}
の構文が最初は奇妙に見えることに同意します。この例では、彼は構造体を宣言してインラインで作成しているため、ブラケットの2番目のセットです。
次のような既存のオブジェクトがある場合:
type Book struct{
}
b := Book{}
のように作成できます。Book構造体は既に宣言されているため、必要なのは1組のブラケットのみです。
done
チャネルは、ワーカーが処理を完了したことを示すwarrior
メソッドから通知を受信するために使用されます。そのため、チャネルは何でもかまいません、例えば:
func warrior(name string, done chan bool) {
select {
case opponent := <-battle:
fmt.Printf("%s beat %s\n", name, opponent)
case battle <- name:
// I lost :-(
}
done <- true
}
func main() {
done := make(chan bool)
langs := []string{"Go", "C", "C++", "Java", "Perl", "Python"}
for _, l := range langs { go warrior(l, done) }
for _ = range langs { <-done }
}
ブール値を受け取るチャネルとしてdone := make(chan bool)
を宣言し、代わりにtrue
の最後にwarrior
を送信します。これは動作します! done
チャネルを他のタイプに定義することもできますが、それは重要ではありません。
1。奇妙なdone <- struct{}{}
とは何ですか?
これは、チャネルに渡される単なる別のタイプです。以下に精通している場合、これは空の構造体です。
type User struct {
Name string
Email string
}
struct{}
はフィールドが含まれていないことを除いて違いはありません。また、struct{}{}
はその中の単なるインスタンスです。最良の機能は、メモリスペースがかからないことです。
2。 forループの使用法
次の行を使用して、バックグラウンドで実行する6つのゴルーチンを作成します。
for _, l := range langs { go warrior(l, done) }
メインゴルーチン(メイン関数が実行される場所)はゴルーチンが完了するのを待たないため、for _ = range langs { <-done }
を使用します。
行の最後を含めない場合は、出力が表示されない可能性があります(子ゴルーチンがfmt.Printf
コードを実行する前にメインゴルーチンが終了し、メインゴルーチンが終了すると、すべての子ゴルーチンが終了します。とにかく走る)。
したがって、すべてのゴルーチンが終了するまで待機し(最後まで実行され、done
チャネルにメッセージを送信します)、終了します。ここのdone
チャネルはブロックされたチャネルです。つまり、<-done
はチャネルからメッセージが受信されるまでここでブロックされます。
バックグラウンドに6つのゴルーチンがあり、forループを使用して、すべてのゴルーチンが実行を終了することを意味するメッセージを送信するまで待機します(done <-struct{}{}
が関数の最後にあるため)。