Подтвердить что ты не робот

Голанг: анонимная структура и пустая структура

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 }
}

[1-й вопрос]

 done <- struct{}{}

Как и зачем нам нужна эта странная структура? Это пустая структура или анонимная структура? Я искал ее, но не смог найти правильный ответ или документацию, чтобы объяснить это.

Оригинальный источник - Эндрю Герранд http://nf.wh3rd.net/10things/#10

Здесь

 make(chan struct{})

done - это канал типа struct {}

Итак, я пробовал с помощью

 done <- struct{}

Но он не работает. Зачем нужны дополнительные скобки для этой строки?

 done <- struct{}{}

[2-й вопрос]

 for _ = range langs { <-done }

Зачем мне нужна эта строка? Я знаю, что эта строка необходима, потому что без этой строки нет вывода. Но почему и что делает эта линия? И что делает необходимым в этом коде? Я знаю, что <-done должен получать значения из канала и отбрасывать полученные значения. Но зачем мне это нужно?

Спасибо!

4b9b3361

Ответ 1

Композитные литералы

Композитные литералы строят значения для структур, массивов, срезов и карты и создавать новое значение каждый раз, когда они оцениваются. Они состоят типа значения, за которым следует скобковый список составных элементы. Элементом может быть одиночное выражение или пара ключ-значение.

struct{}{} представляет собой составной литерал типа struct{}, тип значения, за которым следует скобковый список составных элементов.

for _ = range langs { <-done } ожидает, пока все goroutines для всех langs не отправят сообщения done.

Ответ 2

Обратите внимание, что один интересный аспект использования struct {} для типа, нажатого на канал (в отличие от int или bool), состоит в том, что размер пустой структуры... 0!

См. недавнюю статью Пустая структура "(март 2014 г.) Дэйв Чейни.

Вы можете создать столько struct{}, сколько хотите (struct{}{}), чтобы направить их на ваш канал: ваша память не будет затронута.
Но вы можете использовать его для передачи сигналов между режимами go, как показано на "" Любопытные каналы".

И вы сохраняете все другие преимущества, связанные со структурой:

  • вы можете определить методы на нем (этот тип может быть приемником метода)
  • вы можете реализовать интерфейс (с указанными методами, которые вы просто определяете в своей пустой структуре)
  • как синглтон

в Go вы можете использовать пустую структуру и хранить все свои данные в глобальных переменных. Будет только один экземпляр типа, поскольку все пустые структуры взаимозаменяемы.

См., например, глобальный var errServerKeyExchange в файле, где пустой struct rsaKeyAgreement.

Ответ 3

  • struct{} - это тип (в частности, структура без элементов). Если у вас есть тип Foo, вы можете создать значение этого типа в выражении с помощью Foo{field values, ...}. Объединяя это, struct{}{} - это значение типа struct{}, которое ожидает канал.

  • Функция main порождает warrior goroutines, которые будут записываться в канал done по завершении. Последний блок for читает с этого канала, гарантируя, что main не вернется, пока все goroutines не закончатся. Это важно, потому что программа завершается, когда main завершается, независимо от того, существуют ли другие goroutines.

Ответ 4

Хорошие вопросы,

Вся суть канала структуры в этом сценарии - просто сигнализировать о завершении того, что произошло что-то полезное. Тип канала не имеет большого значения, он мог бы использовать int или bool для достижения того же эффекта. Важно то, что его код выполняется синхронно, когда он делает необходимую бухгалтерию, чтобы сигнализировать и двигаться дальше в ключевых точках.

Я согласен, что синтаксис struct{}{} сначала выглядит нечетным, потому что в этом примере он объявляет структуру и создает ее в строке, следовательно, второй набор скобок.

Если у вас был ранее существующий объект, например:

type Book struct{

}

Вы можете создать его так: b := Book{}, вам нужен только один набор скобок, потому что структура Book уже объявлена.

Ответ 5

Канал

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) в качестве канала, который получает значение bool, и отправляем true в конце warrior. Это работает! Вы также можете определить канал done для любого другого типа, это не имеет значения.

1. Итак, что случилось со странным done <- struct{}{}?

Это просто другой тип, который будет передан каналу. Это пустая структура, если вы знакомы со следующим:

type User struct {
    Name string
    Email string
}

struct{} не имеет значения, кроме того, что он не содержит полей, а struct{}{} - это просто экземпляр из него. Лучшая функция - это не стоит места в памяти!

2. для использования цикла

Мы создаем 6 goroutines для работы в фоновом режиме с помощью этой строки:

    for _, l := range langs { go warrior(l, done) }

Мы используем for _ = range langs { <-done }, потому что главный goroutine (где выполняется основная функция) не ждет завершения goroutins.

Если мы не включим последнюю строку в строку, скорее всего, мы не увидим никаких выходов (потому что главные goroutines завершают работу перед тем, как какой-либо дочерний goroutines выполнит код fmt.Printf, и когда главный goroutine завершит работу, все дочерние goroutines завершатся с ним и будут не имеет шансов на запуск в любом случае).

Итак, мы ждем завершения всех goroutines (он заканчивается и отправляет сообщение в канал done), а затем выходит. Канал done здесь - заблокированный канал, что означает, что <-done будет блокироваться здесь, пока сообщение не будет получено от канала.

У нас есть 6 goroutines в фоновом режиме и мы используем for loop, мы ожидаем, пока все goroutines не отправят сообщение, что означает, что он завершил работу (потому что done <-struct{}{} находится в конце функции).