GO Learning Channel (Channel)

GO series

1. Hello World of GO Learning
2. Introductory Grammar of GO Learning
3. Slicing Operation of GO Learning
4. Map Operation of GO Learning
5. Structure Operation of GO Learning
6. Channel of GO Learning (Channel)
7. GO Learning of Multithreading (goroutine)
8. GO learning function (Function)
9. GO learning interface (Interface)

foreword

According to the company's current tasks, go learning is the only way to go. Although the industry is difficult, but the skills are not overwhelming, we still work hard! ! !
In today's Internet projects, the amount of concurrency is a very important indicator to measure a project. If a project cannot support high concurrency, it is like a supercar with a one-cylinder engine.
Then how to support high concurrency, the first thing to bear the brunt must be multi-threading, then multi-threading will inevitably bring about a problem, data synchronization and security!
Of course, this article does not talk about multi-threading, but first talk about the Channel that supports data synchronization between multiple threads.
Although shared memory can be used for data interaction, shared memory is prone to resource competition in different goroutines, so in order to ensure the correctness of data interaction, locks must be used to lock the memory, but this approach will bring Here comes the performance issue.
The concurrency model of the Go language is CSP (Communicating Sequential Processes), which advocates data interaction between threads through communication and shared memory.

1. Introduction to Channel

Channel in Go language is a special type. As the name suggests, one side comes in and the other side goes out, or the channel is like a queue, which always follows the first in first out (First In Frist Out) rule to ensure the order.
Each channel is a specific type of pipeline, that is, the data type of the element needs to be specified when declaring.

2. Initialize the channel

Channel is a reference type, and the format for declaring a channel type is as follows:

var variable name chan element type

package main

import "fmt"

func main() {
    
    
	// 声明一个空的channel
	var ch1 chan int
	fmt.Printf("初始化ch1: %+v\n", ch1)
	ch1 <- 1
	ch1 <- 2
	ch1 <- 3
	fmt.Printf("%+v\n", ch1)
}

operation result:

PS D:\workspaceGo> go run channelTest.go
初始化ch1: <nil>
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send (nil chan)]:
main.main()
        D:/workspaceGo/channelTest.go:9 +0x73
exit status 2

After the declaration, ch1 accepts elements and reports an error, because the ch1 we declared is actually nil, and sending elements to an empty channel will report an error.
The declared channel needs to be initialized with the make() function before it can be used.

2.1 Unbuffered channel

package main

import "fmt"

func main() {
    
    
	// 声明并且初始化channel
	ch1 := make(chan int)
	fmt.Printf("初始化ch1: %+v\n", ch1)
	ch1 <- 1
}

operation result:

PS D:\workspaceGo> go run channelTest.go
初始化ch1: 0xc0000220c0
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
        D:/workspaceGo/channelTest.go:9 +0x85
exit status 2

Why is there a deadlock error here?
Answer: Because we are creating an unbuffered channel, an unbuffered channel can only send a value if there is another place to accept the value. In the above example, blocking at ch1 <- 1 forms a deadlock, how to solve it? We can use another goroutine to receive the value , as follows:

package main

import "fmt"

func main() {
    
    
	// 声明并且初始化channel
	ch1 := make(chan int)
	// 启动一个 gorountine从通道接收值
	go receive(ch1)
	ch1 <- 1
	ch1 <- 2
	ch1 <- 3
	fmt.Println("发送完成!")
}

func receive(c chan int) {
    
    
	for i := 0; i < 5; i++ {
    
    
		r := <-c
		fmt.Printf("接收到的值为:%+v\n", r)
	}
}

operation result:

PS D:\workspaceGo> go run channelTest.go
接收到的值为:1
接收到的值为:2
接收到的值为:3
发送完成!

In this example, a goruntine is started separately to obtain values ​​from the ch1 channel in a loop, so that values ​​can be continuously put into it through ch1 <- *.

2.2 Buffered channels

You can specify the capacity of the channel when the make() function initializes the channel, as follows:

package main

import (
	"fmt"
	"time"
)

func main() {
    
    
	// 声明并且初始化channel
	ch1 := make(chan int, 5)
	go receive(ch1)
	for i := 0; i < 10; i++ {
    
    
		fmt.Printf("向ch1中发送 %d 个元素 \n", i)
		ch1 <- i
	}
	fmt.Println("发送完成!")
	ch1 <- 6
}

func receive(c chan int) {
    
    
	for i := 0; i < 10; i++ {
    
    
		time.Sleep(time.Second * 3)
		r := <-c
		fmt.Printf("接收到的值为:%+v\n", r)
	}
}

operation result:

PS D:\workspaceGo> go run channelTest.go
向ch1中发送 0 个元素 
向ch1中发送 1 个元素
向ch1中发送 2 个元素
向ch1中发送 3 个元素
向ch1中发送 4 个元素
向ch1中发送 5 个元素
接收到的值为:0
向ch1中发送 6 个元素
接收到的值为:1
向ch1中发送 7 个元素
接收到的值为:2
向ch1中发送 8 个元素
接收到的值为:3
向ch1中发送 9 个元素
接收到的值为:4
发送完成!

The initialization channel is to specify the number of caches. You can put elements within the initialization number into the channel, but if it exceeds the initialization amount, it will block and wait for the channel to be free.

2.3 One-way channel

3. Channel operation

Channels have three operations:

  • Send: Included in the example above
  • Accepted: Included in the example above
  • closure

3.1 Closing the channel

The channel can be closed through the close() function (remember to close the channel after sending and receiving the channel), as follows:

package main
import (
	"fmt"
)
func main() {
    
    
	// 声明并且初始化channel
	ch1 := make(chan int, 1)
	go receive(ch1)
	for i := 0; i < 5; i++ {
    
    
		fmt.Printf("向ch1中发送 %d 个元素 \n", i)
		ch1 <- i
	}
	fmt.Println("发送完成!")
	// 关闭通道
	close(ch1)
}
func receive(c chan int) {
    
    
	for i := 0; i < 10; i++ {
    
    
		r, ok := <-c
		if !ok {
    
    
			fmt.Printf("接收到的值为:%+v\n", r)
		} else {
    
    
			fmt.Println("通道已关闭!")
		}
	}
}

operation result:

PS D:\workspaceGo> go run channelTest.go
向ch1中发送 0 个元素 
向ch1中发送 1 个元素
通道已关闭!
向ch1中发送 2 个元素
通道已关闭!
向ch1中发送 3 个元素
通道已关闭!
向ch1中发送 4 个元素
通道已关闭!
发送完成!

Initialize the channel, the buffer is 1 , when the element is sent to the ch1 channel, the channel will be closed immediately, and only part of the elements can be received in the receive() function.

4. Channel Abnormalities

operate nil non empty null Full Not full
accept deadlock accept success block accept success accept success
send deadlock Sent successfully Sent successfully block Sent successfully
closure panic The shutdown is successful. After the shutdown, a value of 0 is received Closed successfully, received 0 value Closed successfully, received 0 value Closed successfully, received 0 value

V. Summary

The basic initialization and sending, receiving, closing and other operations of the channel (Channel) are basically reflected in this article, and the use is still indirect. Unlike Java, you need to use locks to control the security issues between threads. Although it has good support for multi-threading, the implementation of data sharing between threads is indeed more complicated, and the application is not simple, so you need to dig deeper.
At this stage, I am still in the learning stage of the Go language. There must be some places that have not been considered comprehensively. All the examples in this article are written by myself and passed the execution. If you have any questions, please advise.
Let me know in the comments! ! ! Learn together and progress together! ! !

Guess you like

Origin blog.csdn.net/qq_19283249/article/details/131993360