使用select在Go中发送到通道的好处是什么?

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

What is the benefit of sending to a channel by using select in Go?

问题

在Gorilla websocket的示例目录中,有一个名为hub.go的文件。

https://github.com/gorilla/websocket/blob/master/examples/chat/hub.go

在这里,你可以找到一个在hub类型上定义的方法,它实现了以下功能:

func (h *hub) run() {
    for {
        select {
        case c := <-h.register:
            h.connections[c] = true
        case c := <-h.unregister:
            if _, ok := h.connections[c]; ok {
                delete(h.connections, c)
                close(c.send)
            }
        case m := <-h.broadcast:
            for c := range h.connections {
                select {
                case c.send <- m:
                default:
                    close(c.send)
                    delete(h.connections, c)
                }
            }
        }
    }
}

为什么它不直接在最后一个case中像这样发送到c.send通道?

case m := <-h.broadcast:
    for c := range h.connections {
        c.send <- m
    }
英文:

In the example directory of Gorilla websocket there is a file called hub.go.

https://github.com/gorilla/websocket/blob/master/examples/chat/hub.go

Here you can find a method on the type hub that does this.

func (h *hub) run() {
	for {
		select {
		case c := &lt;-h.register:
			h.connections[c] = true
		case c := &lt;-h.unregister:
			if _, ok := h.connections[c]; ok {
				delete(h.connections, c)
				close(c.send)
			}
		case m := &lt;-h.broadcast:
			for c := range h.connections {
				select {
				case c.send &lt;- m:
				default:
					close(c.send)
					delete(h.connections, c)
				}
			}
		}
	}
}

Why does it not just send to the c.send channel straight up in the last case like this?

case m := &lt;-h.broadcast:
	for c := range h.connections {
		c.send &lt;- m
	}

答案1

得分: 6

这是一种保证非阻塞发送到通道的方法。
如果 c.send 通道当前无法接收新消息,将执行默认分支。
如果没有 select{} 块,发送到无缓冲或完全填满的缓冲通道可能会被阻塞。

英文:

It's method of guaranted nonblocking send to channel.
In case of c.send chan can't accept new messages now, a default branch will be executed.
Without select{} block sending to unbuffered or fully filled buffered channel can be blocked.

答案2

得分: 1

基本的通道发送和接收是阻塞的。然而,我们可以使用selectdefault子句来实现非阻塞的发送、接收,甚至是非阻塞的多路选择。

package main

import "fmt"

func main() {
    messages := make(chan string)
    //[...]

    // 这是一个非阻塞的接收操作。如果`messages`通道中有值可用,
    // `select`语句将选择`<-messages`这个`case`并接收该值。
    // 如果没有可用值,它将立即选择`default`这个`case`。
    select {
    case msg := <-messages:
        fmt.Println("接收到消息", msg)
    default:
        fmt.Println("未接收到消息")
    }

    // 非阻塞的发送操作类似。
    msg := "hi"
    select {
    case messages <- msg:
        fmt.Println("发送消息", msg)
    default:
        fmt.Println("未发送消息")
    }
    //[...]
}

更明确地说,对于非阻塞的“发送”操作,如果已经有接收方在等待消息,那么第一个case将会执行。否则,它将回退到default

英文:

https://gobyexample.com/non-blocking-channel-operations

// Basic sends and receives on channels are blocking.
// However, we can use `select` with a `default` clause to
// implement _non-blocking_ sends, receives, and even
// non-blocking multi-way `select`s.

package main

import &quot;fmt&quot;

func main() {
    messages := make(chan string)
    //[...]

    // Here&#39;s a non-blocking receive. If a value is
    // available on `messages` then `select` will take
    // the `&lt;-messages` `case` with that value. If not
    // it will immediately take the `default` case.
    select {
    case msg := &lt;-messages:
        fmt.Println(&quot;received message&quot;, msg)
    default:
        fmt.Println(&quot;no message received&quot;)
    }

    // A non-blocking send works similarly.
    msg := &quot;hi&quot;
    select {
    case messages &lt;- msg:
        fmt.Println(&quot;sent message&quot;, msg)
    default:
        fmt.Println(&quot;no message sent&quot;)
    }
    //[...]
}

And to be more clear: for a non-blocking "send", the first case will happen if there is a receiver already waiting for a message. Else it falls back to default

huangapple
  • 本文由 发表于 2015年9月21日 19:20:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/32693931.html
匿名

发表评论

匿名网友

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

确定