匿名结构体和空结构体。

huangapple go评论85阅读模式
英文:

anonymous struct and empty struct

问题

以下是翻译好的内容:

package main

import "fmt"

var battle = make(chan string)

func warrior(name string, done chan struct{}) {
    select {
    case opponent := <-battle:
        fmt.Printf("%s 打败了 %s\n", name, opponent)
    case battle <- name:
        // 我输了 :-(
    }
    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/#10

在这里

make(chan struct{})

done 是一个类型为 struct{} 的通道。

所以我尝试了

done <- struct{}

但它不起作用。为什么我需要额外的括号?

done <- struct{}{}


[第二个问题]

for _ = range langs { <-done } 这一行是什么意思?我知道这行是必需的,因为没有它就没有输出。但是为什么以及这行代码是做什么的?为什么在这段代码中它是必需的?我知道 <-done 是从通道 done 接收值并丢弃接收到的值。但为什么我需要这样做?

英文:

http://play.golang.org/p/vhaKi5uVmm

package main

import &quot;fmt&quot;

var battle = make(chan string)

func warrior(name string, done chan struct{}) {
    select {
    case opponent := &lt;-battle:
        fmt.Printf(&quot;%s beat %s\n&quot;, name, opponent)
    case battle &lt;- name:
        // I lost :-(
    }
    done &lt;- struct{}{}
}

func main() {
    done := make(chan struct{})
    langs := []string{&quot;Go&quot;, &quot;C&quot;, &quot;C++&quot;, &quot;Java&quot;, &quot;Perl&quot;, &quot;Python&quot;}
    for _, l := range langs { go warrior(l, done) }
    for _ = range langs { &lt;-done }
}

[1st Question]

 done &lt;- struct{}{}

How and Why do we need this weird-looking struct? Is it empty struct or anonymous struct? I googled it but couldn't find the right answer or documentation to explain about this.

The original source is from Andrew Gerrand's talk
http://nf.wh3rd.net/10things/#10

Here

 make(chan struct{})

done is a channel of type struct{}

So I tried with

 done &lt;- struct{}

But it is not working. Why do I need an extra brackets for this line?

 done &lt;- struct{}{}

[2nd Question]

 for _ = range langs { &lt;-done }

Why do I need this line? I know that this line is necessary because without this line, no output. But Why and what does this line do? And what makes it necessary in this code? I know that &lt;-done is to receive values from the channel done and discard the received values. But why do I need to do this?

答案1

得分: 44

注意,使用struct{}作为推送到通道的类型(而不是int或bool)的一个有趣方面是,空结构体的大小是... 0!

请参阅Dave Cheney于2014年3月发布的文章“The empty struct”。

您可以创建任意数量的struct{}struct{}{})并将它们推送到通道中:您的内存不会受到影响。
但是,您可以将其用于Go协程之间的信号传递,如“Curious Channels”中所示。

finish := make(chan struct{})

由于close(finish)的行为依赖于信号通道的关闭,而不是发送或接收的值,将finish声明为type chan struct{}表示该通道不包含任何值;我们只对其关闭属性感兴趣。

并且您仍然保留与结构体相关的所有其他优势:

  • 您可以在其上定义方法(该类型可以是方法接收器)
  • 您可以实现接口(使用您在空结构体上定义的方法)
  • 作为单例

在Go中,您可以使用空结构体,并将所有数据存储在全局变量中。由于所有空结构体是可互换的,因此只会有一个类型的实例。

例如,在定义空结构体rsaKeyAgreement的文件中,可以看到全局变量errServerKeyExchange

英文:

Note that one interesting aspect of using struct{} for the type pushed to a channel (as opposed to int or bool), is that the size of an empty struct is... 0!

See the recent article "The empty struct" (March 2014) by Dave Cheney.

You can create as many struct{} as you want (struct{}{}) to push them to your channel: your memory won't be affected.
But you can use it for signaling between go routines, as illustrated in "Curious Channels".

> finish := make(chan struct{})
>
> As the behaviour of the close(finish) relies on signalling the close of the channel, not the value sent or received, declaring finish to be of type chan struct{} says that the channel contains no value; we’re only interested in its closed property.

And you retain all the other advantages linked to a struct:

  • you can define methods on it (that type can be a method receiver)
  • you can implement an interface (with said methods you just define on your empty struct)
  • as a singleton

> in Go you can use an empty struct, and store all your data in global variables. There will only be one instance of the type, since all empty structs are interchangeable.

See for instance the global var errServerKeyExchange in the file where the empty struct rsaKeyAgreement is defined.

答案2

得分: 30

【Composite literals】
复合字面量用于构造结构体、数组、切片和映射的值,并在每次评估时创建一个新值。它们由值的类型后跟一个用大括号括起来的复合元素列表组成。一个元素可以是单个表达式或键值对。

struct{}{} 是类型为 struct{} 的复合字面量,类型后跟一个用大括号括起来的复合元素列表。

for _ = range langs { <-done } 等待所有 langs 的 goroutine 发送 done 消息。

英文:

> Composite literals
>
> Composite literals construct values for structs, arrays, slices, and
> maps and create a new value each time they are evaluated. They consist
> of the type of the value followed by a brace-bound list of composite
> elements. An element may be a single expression or a key-value pair.

struct{}{} is a composite literal of type struct{}, the type of the value followed by a brace-bound list of composite elements.

for _ = range langs { &lt;-done } is waiting until all the goroutines for all the langs have sent done messages.

答案3

得分: 10

  1. struct{}是一种类型(特别是没有成员的结构体)。如果你有一个类型Foo,你可以用Foo{字段值,...}的表达式创建一个该类型的值。综合起来,struct{}{}是类型为struct{}的值,这是通道所期望的。

  2. main函数会创建warrior协程,它们会在完成后向done通道写入数据。最后的for块从该通道读取数据,确保只有当所有协程都完成后,main函数才会返回。这很重要,因为程序将在main函数完成后退出,而不管是否还有其他协程在运行。

英文:
  1. struct{} is a type (in particular, a structure with no members). If you have a type Foo, you can create a value of that type in an expression with Foo{field values, ...}. Putting this together, struct{}{} is a value of the type struct{}, which is what the channel expects.

  2. The main function spawns warrior goroutines, which will write to the done channel when they have finished. The last for block reads from this channel, ensuring that main won't return until all the goroutines have finished. This is important because the program will exit when main completes, irrespective of whether there are other goroutines running.

答案4

得分: 6

好的,以下是翻译好的内容:

好问题,

在这种情况下,struct channel 的整个目的只是为了表示某些有用的事情已经发生完成。channel 类型并不重要,他可以使用 int 或 bool 来实现相同的效果。重要的是,他的代码以同步的方式执行,在关键点上进行必要的记录以进行信号传递和继续执行。

我同意 struct{}{} 的语法一开始看起来很奇怪,因为在这个例子中,他声明了一个 struct 并在内部创建它,所以有第二组括号。

如果你有一个预先存在的对象,比如:

type Book struct{

}

你可以这样创建它:b := Book{},只需要一组括号,因为 Book struct 已经被声明。

英文:

Good questions,

The whole point of the struct channel in this scenario is simply to signal the completion that something useful has happened. The channel type doesn't really matter, he could have used an int or a bool to accomplish the same effect. What's important is that his code is executing in a synchronized fashion where he's doing the necessary bookkeeping to signal and move on at key points.

I agree the syntax of struct{}{} looks odd at first because in this example he is declaring a struct and creating it in-line hence the second set of brackets.

If you had a pre-existing object like:

type Book struct{

}

You could create it like so: b := Book{}, you only need one set of brackets because the Book struct has already been declared.

答案5

得分: 4

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)作为接收布尔值的通道,并在warrior函数的末尾发送true。这样可以正常工作!你也可以将done通道定义为任何其他类型,这不会有影响。

1. 那么奇怪的done <- struct{}{}是什么意思?

这只是另一种将值传递给通道的类型。这是一个空结构体,如果你熟悉以下内容:

type User struct {
    Name string
    Email string
}

struct{}除了不包含任何字段外并没有什么不同,而struct{}{}只是其中的一个实例。最好的特点是它不占用内存空间!

2. for循环的用法

我们使用以下代码创建了6个后台运行的goroutine:

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

我们使用for _ = range langs { <-done },因为主goroutine(即main函数运行的goroutine)不会等待子goroutine完成

如果我们不包含最后一行for循环,很有可能我们看不到任何输出(因为主goroutine在任何子goroutine执行fmt.Printf代码之前就退出了,当主goroutine退出时,所有子goroutine也会随之退出,并且不会有任何运行的机会)。

因此,我们等待所有goroutine完成(运行到末尾,并向done通道发送一条消息),然后退出。这里的done通道是一个阻塞通道,这意味着<-done将在此处阻塞,直到从通道接收到一条消息。

我们有6个后台goroutine,并使用for循环,等待直到所有goroutine发送一条消息,表示它已经完成运行(因为done <- struct{}{}位于函数末尾)。

英文:

done channel is used to receive notifications from warrior method that indicates the worker is done processing. So the channel can be anything, for example:

func warrior(name string, done chan bool) {
    select {
    case opponent := &lt;-battle:
        fmt.Printf(&quot;%s beat %s\n&quot;, name, opponent)
    case battle &lt;- name:
        // I lost :-(
    }
    done &lt;- true
}

func main() {
    done := make(chan bool)
    langs := []string{&quot;Go&quot;, &quot;C&quot;, &quot;C++&quot;, &quot;Java&quot;, &quot;Perl&quot;, &quot;Python&quot;}
    for _, l := range langs { go warrior(l, done) }
    for _ = range langs { &lt;-done }
}

We declare done := make(chan bool) as a channel that receives bool value, and send true at the end of warrior instead. This works! You can also define the done channel to any other type, it won't matter.

1. So what is with the weird done &lt;- struct{}{}?

It is just another type that will be passed to channel. This is an empty struct, if you are familiar with the following:

type User struct {
    Name string
    Email string
}

struct{} makes no difference except it contains no fields, and struct{}{} is just an instance out of it. The best feature is it does not cost memory space!

2. for loop usage

We create 6 goroutines to run in the background with this line:

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

We use the for _ = range langs { &lt;-done }, because the main goroutine(where main function runs) does not wait for goroutins to finish.

If we does not include the last for line, chances are we see no outputs(because main goroutines quits before any child goroutines executes fmt.Printf code, and when main goroutine quits, all child goroutines will quit with it, and will not have any chance to run anyway).

So we wait for all goroutines to finish(it runs to the end, and send a message to the done channel), then exits. done channel here is a blocked channel, which means &lt;-done will block here until a message is received from the channel.

We have 6 goroutines in the background, and use for loop, we wait until all goroutines send a message which means it finished running(because the done &lt;-struct{}{} is at the the end of function).

huangapple
  • 本文由 发表于 2013年12月27日 09:37:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/20793568.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定