This is the 24th day of my participation in the August Text Challenge.More challenges in August

Usage syntax for channels

Sending and receiving

Send and receive syntax:

data := <- a // read from channel a  
a <- data // write to channel a
Copy the code

Specifies whether data is sent or received in the direction of the arrow on the channel.

In addition:

v, ok := <- a // Read from a channel
Copy the code
Send and receive are blocked by default

A channel that sends and receives data is blocked by default. When a data is sent to a channel, it is blocked in the send statement until another Goroutine reads from that channel. In contrast, when reading data from a channel, the reading is blocked until a Goroutine writes data to the channel.

The feature of these channels is to help Goroutines communicate effectively without the explicit locks or condition variables that are so common in other programming languages.

Sample code:

package main

import "fmt"

func main(a) {
	var ch1 chan bool       // Declare that there is no creation
	fmt.Println(ch1)        //<nil>
	fmt.Printf("%T\n", ch1) //chan bool
	ch1 = make(chan bool)   // 0xC0000A4000 is referenced data
	fmt.Println(ch1)

	go func(a) {
		for i := 0; i < 10; i++ {
			fmt.Println("In sub-goroutine, I:", i)
		}
		// Write data to the channel to indicate the end of the loop.
		ch1 <- true

		fmt.Println("The end...")

	}()

	data := <-ch1 // Read data from ch1 channel
	fmt.Println("data-->", data)
	fmt.Println("main。。over。。。。")}Copy the code

Running results:

In the above program, we first create a Chan bool channel. Then a sliver of Goroutine is fired and a 10-digit loop is printed. We then write the input true to channel CH1. Then in the main Goroutine, we read the data from CH1. This line of code is blocked, which means that the main Goroutine will not execute to the next line of code until the child Goroutine writes data to the channel. Therefore, we can communicate between a child goroutine and a master goroutine through a channel. When the child goroutine completes, the main goroutine blocks reading data from CH1. This ensures that the child Goroutine completes first. This eliminates the need for time. In the previous program, we either put the main Goroutine to sleep to prevent the main goroutine from exiting. Either a WaitGroup is used to ensure that the child goroutine finishes before the primary goroutine finishes.

Example code: The following code adds sleep to better understand channel blocking

package main

import (
	"fmt"
	"time"
)

func main(a) {
	ch1 := make(chan int)
	done := make(chan bool) / / channel
	go func(a) {
		fmt.Println("Child Goroutine execution...")
		time.Sleep(3 * time.Second)
		data := <-ch1 // Read data from the channel
		fmt.Println("data:", data)
		done <- true} ()// Write data to the channel.
	time.Sleep(5 * time.Second)
	ch1 <- 100

	<-done
	fmt.Println("Main.. over")}Copy the code

Running results:

In another example, this program will print the sum of squares of the units digit of a number.

package main

import (  
    "fmt"
)

func calcSquares(number int, squareop chan int) {  
    sum := 0
    fornumber ! =0 {
        digit := number % 10
        sum += digit * digit
        number /= 10
    }
    squareop <- sum
}

func calcCubes(number int, cubeop chan int) {  
    sum := 0 
    fornumber ! =0 {
        digit := number % 10
        sum += digit * digit * digit
        number /= 10
    }
    cubeop <- sum
} 
func main(a) {  
    number := 589
    sqrch := make(chan int)
    cubech := make(chan int)
    go calcSquares(number, sqrch)
    go calcCubes(number, cubech)
    squares, cubes := <-sqrch, <-cubech
    fmt.Println("Final output", squares + cubes)
}
Copy the code

Running results:

Final output 1536
Copy the code
A deadlock

An important factor to consider when using channels is deadlocks. If a Goroutine is sending data on one channel, it is expected that the other Goroutine should receive the data. If this does not happen, the program will be deadlocked at runtime.

Similarly, if a Goroutine is waiting to receive data from a channel, some other Goroutine will write data to that channel, otherwise the program will deadlock.

Sample code:

package main

func main(a) {  
    ch := make(chan int)
    ch <- 5
}
Copy the code

Error:

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
	/Users/ruby/go/src/l_goroutine/demo08_chan.go:5 +0x50
Copy the code

Mainstream programming languages provide a basic set of synchronization tools, such as locks, condition variables, atomic operations, and so on, to ensure the security and consistency of data shared between multiple threads. Not surprisingly, the Go language standard library provides these synchronization mechanisms in much the same way as other languages. In addition to these basic synchronization methods, Go provides a new synchronization mechanism: Channel, which in Go is a basic type like INT, float32, etc. A Channel can be thought of as a Channel that can pass a certain type of data between multiple Goroutines. Channels in Go are very similar to BlockingQueue in Java, both in their implementation mechanism and usage scenario.

Close the channel

The sender can close the channel to inform the receiver that no more data will be sent to the channel.

close(ch)
Copy the code

The receiver can use additional variables to check if the channel is closed when receiving data from the channel.

Grammatical structure:

v, ok := <- ch  
Copy the code

Similar to the map operation, stores key and value pairs

V,ok := map[key] // Obtain value from map based on key. If key exists, v is the corresponding data. If key does not exist, v is the default value

In the above statement, if ok is true, a data value was successfully read from the channel. If OK is false, it means we are reading from a closed channel. The value read from a closed channel will be zero for the channel type.

For example, if the channel is an int channel, the value received from the closed channel will be 0.

Sample code:

package main

import (
	"fmt"
	"time"
)

func main(a)  {
	ch1 := make(chan int)
	go sendData(ch1)
	/* Block the main goroutine by reading it once: Loop read reads one at a time, blocks one, subroutine, writes one, unblocks send, closes the channel --> receiver, receives data of the type zero, and false */
	// Get channel data in the main program
	for{
		time.Sleep(1*time.Second)
		v, ok := <- ch1 // Other goroutine, which displays the call to close the channel.
		if! ok{ fmt.Println("All the data has been read,", ok)
			break
		}
		fmt.Println("Fetch data:",v, ok)
	}

	fmt.Println("main... over....")}func sendData(ch1 chan int)  {
	// Sender: 10 pieces of data
	for i:=0; i<10; i++ { ch1 <- i// Write I to the channel
	}
	close(ch1) // The ch1 channel is closed.
}
Copy the code

The results

In the above program, send Goroutine writes 0 through 9 to the CHL channel and then closes the channel. There’s an infinite loop in the main function. It checks if the channel is closed using the variable OK after sending data. If OK is false, it means the channel is closed, so the loop ends. You can also print the received value and the ok value.