头图

background

I believe everyone is familiar with the built-in data structure channel of Go. Channel plays a very important role in the Go language.

  • Channels can help achieve communication and synchronization between goroutines.
  • The Go language implements the CSP (Communicating Sequencial Process) model through goroutines and channels.

Robe Pike, one of the inventors of the Go language, said the following:

Don't communicate by sharing memory, share memory by communicating.

The hidden meaning in this sentence is actually to hope that Go developers use channels to communicate between goroutines.

There are 2 types of channels:

  • bi-directional channel: you can send data to the channel and receive data from the channel
  • uni-directional channel

    • send-only channel: You can only send data to the channel and cannot receive data from the channel, otherwise it will compile an error
    • receive-only channel: can only receive data from the channel, not send data to the channel, otherwise it will compile an error

A typical usage scenario of a one-way channel is as a function or method parameter to control that data can only be sent to or received from the channel to avoid misoperation.

 package main

import (
    "fmt"
)

// send-only channel
func testSendChan(c chan<- int) {
    c <- 20
}

// receive-only channel
func testRecvChan(c <-chan int) {
    result := <-c
    fmt.Println("result:", result)
}

func main() {
    ch := make(chan int, 3)
    testSendChan(ch)
    testRecvChan(ch)
}

For example, in the above program, the parameter of testSendChan is a send-only channel, and the parameter of testRecvChan is a receive-only channel.

The actual parameter ch is a two-way channel, and the Go runtime will convert the two-way channel into a one-way channel and pass it to the corresponding function.

question

The above example uses a one-way channel as a function parameter, a two-way channel as a function parameter, and the make function creates a two-way channel.

So can we use make to create a one-way channel? Is there any actual usage scenario for the one-way channel created by make? Are there potential pits?

Let's take a look at the following two questions:

Topic 1

 ch := make(<-chan int)
close(ch)
fmt.Println("ok")
  • A: print ok
  • B: Error at runtime: fatal error - deadlock
  • C: runtime error: painic
  • D: Compilation failed

Topic 2

 c := make(chan<- int)
close(c)
fmt.Println("ok")
  • A: print ok
  • B: Error at runtime: fatal error - deadlock
  • C: runtime error: painic
  • D: Compilation failed

You can pause for a while and think about what the answers to these two questions will be.

Parse

Answer

  • The answer to question 1 is D. Question 1 creates a receive-only channel, which can only receive values from the channel and cannot send values to the channel.

    The receive-only channel cannot be closed. If the close operation is performed, the following error will be reported:

 ./main.go:9:7: invalid operation: close(ch) (cannot close receive-only channel)
  • The answer to question 2 is A. Question 2 creates a send-only channel, which can only send values to the channel, but cannot receive values from the channel.

    For send-only channels, it can be closed normally.

Why can't receive-only channel be closed, but send-only channel can be closed?

This is because a receive-only channel means that it can only receive data from this channel. The user has only read permission to this channel, but no write permission to the channel, that is, it cannot send data to the channel or perform close operations on the channel.

So the answer to question 1 is D and the answer to question 2 is A.

Derived problem

 // send-only channel
func testSendChan(c chan<- int) {
    c <- 20
}

// receive-only channel
func testRecvChan(c <-chan int) {
    result := <-c
    fmt.Println("result:", result)
}

func main() {
    ch := make(chan int, 3)
    testSendChan(ch)
    testRecvChan(ch)
}

The above code is a typical usage scenario of a one-way channel. The function parameter is a two-way channel, and the function parameter is a one-way channel. We can send data to the channel through the send-only channel, and receive data from the channel through the receive-only channel. This is easy to understand. But it should be noted that the function argument here is a bidirectional channel .

Careful developers may have a question. For the above two questions, make creates a one-way channel, and either can only send data to this channel, or can only receive data from this channel, unlike make in the above code. The created channel is a bidirectional channel.

The question is, is there any practical use for the one-way channel created by make ?

For example, the send-only channel created by make(chan<- int) can only send data to this channel, but cannot read data from this channel, what's the use?

For example, the receive-only channel created by make(<-chan int) can only receive data from this channel, but cannot send data to this channel, what's the use?

Regarding this issue, in fact, there have been relatively big disputes among the big brothers of the Go language.

Brad Fitzpatrick of the Golang team complained that the compiler should report an error directly to make to create a one-way channel, because the one-way channel created by make is useless.

 I'm surprised the compiler permits create send- or receive-only channels:
------
package main

import "fmt"

func main() {
    c := make(<-chan int)
    fmt.Printf("%T\n", c)
}

------

Is that an accident?

I can't see what utility they'd have.

The feedback given by Russ Cox , the head of the Go language, is: I think there is no clear reason for the compiler to prohibit make from creating a one-way channel, because if there is an illegal operation on a one-way channel, the compiler will still report an error, and let the compiler directly prohibit make from creating it. One-way channels will increase the complexity of Go design, and it is unnecessary and worth the loss.

 I can't see a reason to disallow it either though.

All I am saying is that explicitly disallowing it adds
complexity to the spec that seems unnecessary.
What bugs or problems does it avoid to make this
special exclusion in the rules for make?
Russ

Recommended reading

Summarize

  1. For channel , sending data to channel and receiving data from channel will block.
  2. For nil channel and buffered channel , the mechanism of sending and receiving data is shown in the following table:

    channel nil empty Not empty not full full
    send data to channel block Sent successfully Sent successfully block
    Receive data from channel block block Received successfully Received successfully
    close channel panic Closed successfully Closed successfully Closed successfully
  3. channel After being closed:

    • Sending data to the closed channel will trigger a panic.
    • To receive data from the closed channel , it will read the data in channel first. If the data is read, continue to read data from channel to get the zero value of the element type stored in channel .

       data, ok := <- c

      对于上面的代码,如果channel c关闭了, c里读数据,当c里还有数据时, data is the corresponding read value, the value of ---5b9166b37368899c464e67166163e5dd ok is true . c读完了,那data就是零值, ok的值是false

    • channel After being closed, if it is closed again, it will cause panic.
  4. The operating mechanism of select is as follows:

    • Select an executable non-blocking case branch, if multiple case branches are not blocked, one will be randomly selected case branch execution, and case The order in which branches are written in the code does not matter.
    • If all case branches are blocked, the default branch will be executed.
    • If there is no default branch, then select will block until there is a case branch that does not block.

open source address

Articles and sample code are open sourced on GitHub: Beginner, Intermediate, and Advanced Tutorials in Go .

Official account: coding advanced. Follow the official account to get the latest Go interview questions and technology stacks.

Personal website: Jincheng's Blog .

Zhihu: Wuji .

Welfare

I have compiled a back-end development learning material package for you, including programming language entry to advanced knowledge (Go, C++, Python), back-end development technology stack, interview questions, etc.

Follow the official account "coding advanced", send a message backend to receive a gift package, this information will be updated from time to time, and add information that I think is valuable. You can also send a message "join the group " to communicate and learn with your peers and answer questions.

References


coding进阶
116 声望18 粉丝