SoFunction
Updated on 2025-03-05

Detailed explanation of the unbuffered channel and buffered channel of Go language channel

1. Channel definition

Communication and management between multiple coroutines can be used using the Channel type provided by the Go language. A channel is a special data structure that can pass data between coroutines, thereby enabling communication and synchronization between coroutines. Multiple coroutines can read and write the same channel at the same time, and pass and share data through the channel.

Channels follow the first in first out principle to ensure the order in which data is sent and received. A channel is a special data type. Before use, channel variables must be defined and created. The syntax for defining a channel is as follows:

var name chan type

The syntax format description is as follows:

1) var is a Go language keyword, used to define variables.

2) name is the channel variable name and can be named by yourself.

3) chan is a Go language keyword, defining variables as channel type.

4) Type is the data type stored in the channel.

After the channel is defined, you also need to use the keyword make to create the channel. The creation syntax of the channel is as follows:

name := make(chan type, num)

The syntax format description is as follows:

1) Name is the name of the channel variable and can be named by itself.

2) make is a Go language keyword, used to create channels.

3) The chan of chan type is the Go keyword, and type is the data type that the channel can store.

4) num is the upper limit of the number of data stored in the channel.

In actual programming, we can directly use the keyword make to create a channel, which can eliminate the process of defining a channel. The example code is as follows:

// Define and create channels    var ch chan string
    ch = make(chan string)
    // Create a channel directly without defining it    ch := make(chan string)

After the channel is created, the channel is used to complete the write and read data operations. Writing and reading data in the channel needs to be implemented by the <- operator, and the instructions are as follows:

// Build a channel    ch := make(chan string)
    // Write data to the channel    ch &lt;- "Hello"
    // Get data from the channel and assign variable s    s := &lt;- ch

2. No buffered channel

Unbuffered channels are a common channel type in Go, also known as synchronous channels or blocked channels. The characteristic of an unbuffered channel is that when sending and receiving data, another coroutine must perform the opposite operation at the same time, otherwise it will block the current coroutine. Specifically, the characteristics of unbuffered channels are as follows:

  • The sending and receiving operations are synchronized, that is, the sending operation must wait until the receiving operation is completed before it can be executed, and the receiving operation must also wait until the sending operation is completed before it can be executed.
  • The capacity of the unbuffered channel is 0, that is, data can only be passed when the send and receive operations are in progress at the same time, otherwise the current coroutine will be blocked.
  • Data transfer in unbuffered channels is carried out in a first-in-first-out order, that is, the transmitted data will be received in the order of transmission. Unbuffered channels can be used for synchronization and communication between coroutines. For example, in producer and consumer mode, unbuffered channels can be used to pass data, thereby ensuring synchronization and mutual exclusion between producer and consumer. At the same time, the use of unbuffered channels can also avoid data competition issues, thereby improving the security and reliability of the program.

Channels are created through the keyword make. During the creation process, if the parameter num is not set, it is considered to create an unbuffered channel. Unbuffered Channel refers to a channel that does not have the ability to save data before obtaining data. This type of channel requires two Goroutines to be executed at the same time to complete the write and obtain operations.

If two Goroutines are not prepared at the same time, one Goroutine will perform a write or get operation in a blocking and waiting state, and the other Goroutine will be unable to perform a write or get operation, and the program will prompt an exception. The interactive behavior of this type of channel to perform write and get is synchronized, and no operation can exist separately from the other operation.

When we use unbuffered channels, we must pay attention to the operation of channel variables to ensure that there are two or more Goroutines in the program that perform channel read and write operations at the same time. The read and write operations must be read and write, and we cannot only read, not write or write, as follows:

// Only write data, not read    ch := make(chan string)
    ch &lt;- "Tom"
    ("wait goroutine")
    // Only read data, not write    ch := make(chan string)
    &lt;- ch
    ("wait goroutine")

If the channel data is only written and not read or read but not written, it will prompt fatal error: all goroutines are sleep–deadlock exception. If you need to implement channel data acquisition timeout detection, you can use the keyword select to implement it.

If there is only one Goroutine in the program, using the channel to read and write data will also cause exceptions. For example, writing data to the channel in the main function main() and then reading the channel data. The example is as follows:

package main
    import (
        "fmt"
    )
    func main() {
        // Build a channel        ch := make(chan string)
        // Write channel data        ch &lt;- "Tom"
        // Read channel data        &lt;-ch
        ("wait goroutine")
    }

If an exception occurs when sending and receiving data, a program exception will be raised. For example, if we close the channel before sending data, a runtime exception will be raised. To avoid this, we can usedeferThe statement closes the channel before the function exits. For example:

func main() {
    ch := make(chan int)
    defer close(ch) // Use defer to close the channel    go func() {
        ("Start sending messages...")
        ch &lt;- 1
        ("Message sending is complete.")
    }()
    ("Start receiving messages...")
    msg := &lt;-ch
    ("The message received is: %d\n", msg)
    ("Message reception is complete.")
}

3. With buffered channel

A buffered channel is a channel that can store one or more data before being acquired. This type of channel does not force Goroutine to be written and retrieved simultaneously. Only when there is no data in the channel, the acquisition action will block; only when the channel has no available buffer to store data, the write action will block.

On the basis of unbuffered channels, a buffered channel can be formed by adding a limited-sized storage space to the channel. The buffered channel does not need to wait for acquisition to perform the next round of writes again when writing, and there will be no blockage. Blockage will occur only when the storage space is full. Similarly, if there is data in the buffered channel, there will be no blockage during acquisition, and the channel will not block until there is no data in the channel to be read.

From the perspective of channel definition, the difference between buffered and unbuffered channels is the parameter num. When creating a channel, if the parameter num is not set, the default parameter value is 0, and the channel is an unbuffered channel, so the writing and obtaining data must be carried out simultaneously to avoid abnormalities due to blockage; if the parameter num is greater than 0, the writing and obtaining data does not need to be performed synchronously, because the channel has enough space to store data.

Since there is no read and write synchronization limitation on buffered channels, we can perform multiple writes and fetch operations in the same Goroutine. The specific example is as follows:

package main
    import "fmt"
    func main() {
        // Create an integer channel with 3 element buffer sizes        ch := make(chan int, 3)
        // Check the size of the current channel        (len(ch))
        // Send 3 integer elements to the channel        for i := 0; i &lt; 3; i++ {
             ch &lt;- i
        }
        // Check the size of the current channel        (len(ch))
        for i := 0; i &lt; 3; i++ {
             (&lt;-ch)
        }
        // Check the size of the current channel        (len(ch))
        // Check the capacity of the current channel        (cap(ch))
    }

The above code is explained as follows:

1) 3 loops were executed through for, each loop writes the variable i to the channel, and then gets the data from the channel through 3 loops and outputs.

2) When the channel writes and reads data, use the len() function to obtain the existing data amount of the channel and determine whether the current data amount stored in the channel has reached the upper limit. This can prevent the program from prompting an exception when running.

3) Use the cap() function to get the capacity size of the channel, that is, get the size of the parameter num that creates the channel make(). Buffered channels are similar to unbuffered channels in many characteristics. Unbuffered channels can be regarded as buffered channels with length 0.

According to this feature, buffered channels will block in the following cases:

1) When the stored data with buffered channel reaches the upper limit, writing data again will block and cause an exception.

2) When the buffered channel does not store data, the acquisition of data will block and cause an exception.

Why does Go language limit the length of the channel? Because there must be write and get operations using channels between multiple Goroutines, a typical example of this pattern type is the producer consumer pattern. If the channel length is not limited, when the write data speed is greater than the acquisition speed, the memory will continue to swell until the application crashes. Therefore, limiting the length of the channel is conducive to constraining the data production speed. The production data volume must be within the range of data consumption speed + channel length so that the data can be processed normally.

This is the end of this article about the detailed explanation of the unbuffered channels and buffered channels of Go language channels. For more related Go channel content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!