Concurrent programming based on go language, channel

Channel
simply executes functions concurrently is meaningless. Data needs to be exchanged between functions to reflect the meaning of concurrent execution of functions.

Although shared memory can be used for data exchange, shared memory is prone to race conditions in different goroutines. In order to ensure the correctness of data exchange, a mutex must be used to lock the memory, which is bound to cause performance problems.

The concurrency model of the Go language is CSP (Communicating Sequential Processes), which advocates communication through shared memory rather than shared memory.

If goroutine is the concurrent execution body of Go program, channel is the connection between them. A channel is a communication mechanism that allows one goroutine to send a specific value to another goroutine.

Channel in Go language is a special type. The channel is like a conveyor belt or queue, always following the First In First Out (First In First Out) rule to ensure the order of sending and receiving data. Each channel is a specific type of conduit, that is, when you declare the channel, you need to specify the element type for it.

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

Create a channel
channel is a reference type, the null value of the channel type is nil

package main

import "fmt"

func main() {
    
    
	//var 变量 chan 元素类型
	var ch1 chan int
	fmt.Printf("%T,%v\n", ch1, ch1)

	ch2 := make(chan int, 3)
	fmt.Printf("%T,%v\n", ch2, ch2)
}

Channel operation The
channel has three operations: send, receive, and close.

Both sending and receiving use the <- symbol.

package main

import "fmt"

func main() {
    
    
	ch := make(chan int, 1)
	//发送
	ch <- 10
	// 接收
	i, ok := <-ch
	if !ok {
    
    
		fmt.Printf("%v", ok)
		return
	}
	fmt.Printf("%v", i)
	// 关闭
	close(ch)
}

// 对一个关闭的通道再发送值就会导致panic。
// 对一个关闭的通道进行接收会一直获取值直到通道为空。
// 对一个关闭的并且没有值的通道执行接收操作会得到对应类型的零值。
// 关闭一个已经关闭的通道会导致panic。

Unbuffered channel

import "fmt"

func main() {
    
    
	ch := make(chan int)
	ch <- 10
	c := <-ch
	fmt.Println(c)
}

Report an error

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
        D:/go/src/liqunkeji.com/dlq/main.go:7 +0x65
exit status 2

Why is there a deadlock error?

Because we use ch := make(chan int) to create an unbuffered channel, an unbuffered channel can only send a value when someone receives it. Just like the area where you live does not have express cabinets and collection points, the courier must deliver the item to you when calling you. Simply put, the unbuffered channel must be received before it can be sent.

The above code will block the line of code ch <-10 to form a deadlock, so how to solve this problem?

package main

import (
	"fmt"
	"sync"
)

var wy sync.WaitGroup

func chanan(c chan int) {
    
    
	defer wy.Done()
	ch := <-c
	fmt.Println(ch)
}


func main() {
    
    
	wy.Add(1)
	ch := make(chan int)
	go chanan(ch)
	ch <- 10
	wy.Wait()
}

The sending operation on the unbuffered channel will block until another goroutine performs the receiving operation on the channel, then the value can be sent successfully, and the two goroutines will continue to execute. On the contrary, if the receiving operation is performed first, the goroutine of the receiver will block until another goroutine sends a value on the channel.

Using an unbuffered channel for communication will result in synchronization of the sending and receiving goroutines. Therefore, unbuffered channels are also called synchronous channels.

Buffered channels
Another way to solve the above problems is to use buffered channels. We can specify the capacity of the channel when using the make function to initialize the channel, for example:

package main

import "fmt"

func main() {
    
    
	ch := make(chan int, 1)
	ch <- 10
	c := <-ch
	fmt.Println(c)
}

As long as the channel capacity is greater than zero, then the channel is a buffered channel, and the channel capacity represents the number of elements that can be stored in the channel. Just like the express cabinet in your community has only so many grids, if the grids are full, it will not fit, and it will be blocked. When someone takes away a courier, you can put one in it.

We can use the built-in len function to get the number of elements in the channel, and the cap function to get the capacity of the channel, although we rarely do this

for range cyclically take values ​​from the channel

package main

import "fmt"

func main() {
    
    
	ch1 := make(chan int)
	ch2 := make(chan int)
	go func() {
    
    
		for i := 0; i <= 99; i++ {
    
    
			ch1 <- i
		}
		close(ch1)
	}()
	go func() {
    
    
		for {
    
    
			v, ok := <-ch1
			if !ok {
    
    
				break
			}
			ch2 <- v * v
		}
		close(ch2)
	}()

	for i := range ch2 {
    
    
		fmt.Println(i)
	}
}

From the above example, we see that there are two ways to determine whether the channel is closed when receiving the value, but we usually use the for range method. Use for range to traverse the channel and exit for range when the channel is closed.
One-way channel
Sometimes we pass the channel as a parameter between multiple task functions. Many times we use the channel in different task functions to restrict it, such as restricting the channel to only send or only receive in the function .

Go language provides a one-way channel to deal with this situation. For example, we modify the above example as follows:

package main

import "fmt"

func count(out chan<- int) {
    
    
	for i := 0; i < 100; i++ {
    
    
		out <- i
	}
	close(out)
}

func squar(out chan<- int, in <-chan int) {
    
    
	for i := range in {
    
    
		out <- i * i
	}
	close(out)
}

func prin(out <-chan int) {
    
    
	for i := range out {
    
    
		fmt.Println(i)
	}
}

func main() {
    
    
	ch1 := make(chan int)
	ch2 := make(chan int)
	go count(ch1)
	go squar(ch2, ch1)
	prin(ch2)
}

among them,

chan<- int is a write-only one-way channel (only int type values ​​can be written to it), which can be sent but not received;
<-chan int is a read-only one-way channel (only from It reads int type value), it can perform receiving operation but cannot perform sending operation.
In the function parameter transfer and any assignment operation, the bidirectional channel can be converted to the unidirectional channel, but the reverse is not possible.

Channel summary
// channel nil is not empty but full is not full

// Receiving blocking receiving value blocking receiving value receiving value

// send blocking sending value sending value blocking sending value

// Close the panic. After reading the data, it returns a zero value. After reading the data, it returns a zero value. After reading the data, it returns a zero value. After reading the data successfully, it returns a zero value
.

Guess you like

Origin blog.csdn.net/weixin_44865158/article/details/115003214