web-dev-qa-db-ja.com

Golang:匿名構造体と空の構造体

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は、完了したチャネルから値を受信し、受信した値を破棄します。しかし、なぜこれを行う必要があるのですか?

ありがとう!

23
user2671513

複合リテラル

複合リテラルは、構造体、配列、スライス、マップの値を構築し、評価されるたびに新しい値を作成します。これらは、複合要素の波括弧で区切られたリストが後に続く値のタイプで構成されます。要素は、単一の式またはキーと値のペアです。

struct{}{}struct{}型の複合リテラルであり、値の型に複合要素の波括弧で区切られたリストが続きます。

for _ = range langs { <-done }は、すべてのlangsのすべてのゴルーチンがdoneメッセージを送信するまで待機しています。

26
peterSO

(intまたはboolではなく)チャネルにプッシュされる型にstruct {}を使用する興味深い側面の1つは、size空の構造体は... 0!

最近の記事「 The empty struct "(March 2014)by Dave Cheney 」を参照してください。

struct{}好きなように(struct{}{})それらをあなたのチャンネルにプッシュします:あなたの記憶は影響を受けません。
ただし、「 Curious Channels 」に示すように、goルーチン間のシグナリングに使用できます。

そして、構造体にリンクされている他のすべての利点を保持します。

  • メソッドを定義できます(そのタイプはメソッドレシーバーになります)
  • インターフェースを実装できます(空の構造体で定義した上記のメソッドを使用)
  • as シングルトン

goでは、空の構造体を使用して、すべてのデータをグローバル変数に保存できます。すべての空の構造体は交換可能であるため、型のインスタンスは1つだけです。

たとえば、 empty struct errServerKeyExchange が定義されているファイルの global var rsaKeyAgreement を参照してください。

34
VonC
  1. struct{}は型(特に、メンバーのない構造体)です。タイプFooがある場合、Foo{field values, ...}を使用して式にそのタイプの値を作成できます。これをまとめると、struct{}{}struct{}型の値であり、これはチャネルが期待するものです。

  2. main関数はwarriorゴルーチンを生成し、終了するとdoneチャネルに書き込みます。最後のforブロックはこのチャネルから読み取り、mainがすべてのゴルーチンが終了するまで戻らないようにします。実行中の他のゴルーチンがあるかどうかに関係なく、mainが完了するとプログラムが終了するため、これは重要です。

8

良い質問、

このシナリオでの構造体チャネルの全体のポイントは、何か有用なことが起こったことを完了に知らせることです。チャネルの種類は実際には重要ではなく、intまたはboolを使用して同じ効果を達成できた可能性があります。重要なのは、彼のコードが同期化された方法で実行され、重要なポイントで信号を送り、先に進むために必要な簿記を行っていることです。

struct{}{}の構文が最初は奇妙に見えることに同意します。この例では、彼は構造体を宣言してインラインで作成しているため、ブラケットの2番目のセットです。

次のような既存のオブジェクトがある場合:

type Book struct{

}

b := Book{}のように作成できます。Book構造体は既に宣言されているため、必要なのは1組のブラケットのみです。

3
Ralph Caraveo

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{}{}が関数の最後にあるため)。

1
cizixs