Channel in Go language is used for goroutine communication

A channel is a data structure used to transfer data. The design ensures that only one goroutine can receive or put data from it at the same time. Both sending and receiving are atomic operations and will not be interrupted.

Channel in Go language is a special type. At any time, only one goroutine can access the channel to send and get data. The goroutines can communicate through channels.

Channels can be used to synchronize operation and communication between two goroutines by passing a specified type of value. The operator <- is used to specify the direction of the channel, sending or receiving. If the direction is not specified, it is a two-way channel.

ch <- v    // 把 v 发送到通道 ch

v := <-ch  // 从 ch 接收数据,并把值赋给 v          

Declare channel type

The channel itself needs a type to be modified, just like the slice type needs to identify the element type. The element type of the channel is the type of data transmitted inside it, declared as follows:

var channel variable chan channel type

The null value of chan type is nil, and it can be used only after declaration with make.

Create channel

To declare a channel, use the chan keyword. The channel must be created before use. The channel is a reference type and needs to be created with make:

Channel instance: = make (chan data type)

  • Data type: The type of element transmitted in the channel.
  • Channel instance: channel handle created by make.

Example:

ch1 := make(chan int)                 // 创建一个整型类型的通道
ch2 := make(chan interface{
    
    })         // 创建一个空接口类型的通道, 可以存放任意格式

type Equip struct{
    
     /* 字段 */ }
ch2 := make(chan *Equip)             // 创建Equip指针类型的通道, 可以存放*Equip

goroutine

Go language supports concurrency, we only need to open goroutine through the go keyword. A goroutine is a lightweight thread, and the scheduling of a goroutine is managed by the Golang runtime.

Go allows the use of a go statement to start a new runtime thread, a goroutine, to execute a function with a different, newly created goroutine. All goroutines in the same program share the same address space.

E.g:

go a(x, y, z)

This opens a new goroutine.

Example:

func say(s string) {
    
    
    for i := 0; i < 5; i++ {
    
    
            time.Sleep(100 * time.Millisecond)
            fmt.Println(s)
    }
}

func main() {
    
    
    go say("world")
    say("hello")
}

sync.waitGroup
waits for the execution of all goroutines to complete, and blocks the execution of the main thread until all goroutines are executed.

GOMAXPROCS
calls runtime.GOMAXPROCS() to set the maximum number of CPU cores that can be calculated in parallel and returns the previous value.

aisle

If goroutine is a concurrency of Go language programs, then channels are the communication mechanism between them. A channel is a communication mechanism that allows one goroutine to send value information to another goroutine through it. Each channel has a special type, which is the type of data that channels can send. A channel that can send int data is generally written as chan int.

Go language advocates the use of communication instead of shared memory. When a resource needs to be shared between goroutines, the channel sets up a pipeline between goroutines and provides a mechanism to ensure synchronous data exchange.
When declaring the channel, you need to specify the type of data to be shared. The values ​​or pointers of built-in types, named types, structure types, and reference types can be shared through channels.

The method of communication here is to use channels, as shown in the following figure:

Insert picture description here
When there are a lot of people in public places such as subway stations, canteens, and toilets, everyone has developed the habit of queuing in order to avoid the inefficient use of resources and exchange processes caused by crowding and queue jumping.
The same is true for code and data. Multiple goroutines compete for data, which will inevitably cause inefficiency in execution. The way to use queues is the most efficient, and channels are a queue-like structure.

Channel purpose

  • Use channels to synchronize goroutines
  • Use asynchronous pipelines to protect critical resources (only those who get the token can use it, and return the pipeline after use)
  • Use pipes to send events

Sync channel

A channel with a buffer length of 0 is called a synchronous pipe and can be used to synchronize two routines

  • The sending operation is blocked until the receiving end is ready to receive
  • The receiving operation is blocked until the sender is ready to send

Asynchronous channel

A channel with a buffer length greater than 0 is called an asynchronous pipe. Asynchronous channel is to set a buffer value for the channel

  • When the buffer is not full, the sending operation is not blocked.
  • Before the buffer is read, the receiving operation is not blocked.

The range keyword is used to traverse the read data, similar to arrays or slices. The format is as follows:

v, ok := <-ch

If the channel receives no data, ok will be false, and then the channel can be closed using the close() function.

Example:

    // 声明通道    
    ch := make(chan int)
	
    var ch1 chan int       // ch1是一个正常的channel,不是单向的
    var ch2 chan<- float64 // ch2是单向channel,只用于写float64数据
    var ch3 <-chan int     // ch3是单向channel,只用于读取int数据
	
	// 通道可以设置缓冲区,通过 make 的第二个参数指定缓冲区大小
	
	ch := make(chan int, 2)
	
	// 因为 ch 是带缓冲的通道,我们可以同时发送两个数据
	// 而不用立刻需要去同步读取数据
	ch <- 1
	ch <- 2
	
	// 获取这两个数据
	fmt.Println(<-ch)
    fmt.Println(<-ch)

func fibonacci(n int, c chan int) {
    
    
    x, y := 0, 1
    for i := 0; i < n; i++ {
    
    
            c <- x
            x, y = y, x+y
    }
    close(c)
}


func main() {
    
    
    c := make(chan int, 10)
    go fibonacci(cap(c), c)
    // range 函数遍历每个从通道接收到的数据,因为 c 在发送完10个数据之后就关闭了通道。
    // 所以这里我们 range 函数在接收到 10 个数据之后就结束了。
    // 如果上面的 c 通道不关闭,那么 range 函数就不会结束,从而在接收第 11 个数据的时候就阻塞了。
 
    for i := range c {
    
    
            fmt.Println(i)
    }
}

The execution result is:

0
1
1
2
3
5
8
13
21
34

Channel buffer

By default, the channel has no buffer. The sender sends data, and at the same time there must be corresponding receive data for the receiver.

The channel can set the buffer. The buffer size is specified by the second parameter of make:

ch := make(chan int, 100)

The channel with buffer allows the data transmission of the sender and the data acquisition of the receiver to be in an asynchronous state, which means that the data sent by the sender can be placed in the buffer and can wait for the receiver to obtain the data, instead of immediately requiring the receiver to obtain the data .

However, because the size of the buffer is limited, there must be a receiving end to receive the data, otherwise, once the buffer is full, the data sending end can no longer send data.

Note:
If the channel is not buffered, the sender will block until the receiver receives the value from the channel. If the channel is buffered, the sender will block until the sent value is copied into the buffer;
if the buffer is full, it means that it needs to wait until a receiver obtains a value. The receiver will block until there is a value to receive.

Example:

package main



import "fmt"



func main() {
    
    

        //定义了一个可以存储整数类型的带缓冲通道
        // 缓冲区大小为2

        ch := make(chan int, 2)


        // 因为 ch 是带缓冲的通道,我们可以同时发送两个数据
        // 而不用立刻需要去同步读取数据

        ch <- 1

        ch <- 2


        // 获取这两个数据

        fmt.Println(<-ch)

        fmt.Println(<-ch)

}

The execution result is:

1
2

Guess you like

Origin blog.csdn.net/zp17834994071/article/details/108760099