What is the Channel

A Channel is a communication mechanism provided for Goroutine in Go. A Channel is typed and directional. A Channel can be likened to a Unix pipe.

The use of the channel

The channel to create

A channel literally means “channel”, similar to a pipe in Linux. The syntax for declaring channels is as follows:

Chan T // Can receive and send data of type T chan<-float64 // Can only be used for sendingfloat64 <-chan int // Can only be used to receive data of int typeCopy the code

Use make to initialize a Channel, and you can set the capacity:

make(chan int, 100)
Copy the code

Because a channel is a reference type, its value is nil until it is initialized, which is done using the make function. We can pass it an int that represents the size of the channel buffer, and construct a buffered channel. A channel that does not pass or pass 0 constructs a non-buffered channel.

Channels are divided into two types: buffered and unbuffered. Operations on unbuffered channels can actually be considered “synchronous mode”, while buffered channels are called “asynchronous mode”.

In synchronous mode, the sender and receiver must be ready for synchronization. Data can be transmitted between them only when both are ready. Otherwise, either party that performs a send or receive operation will be suspended, waiting for the other party to appear before waking up.

In asynchronous mode, both send and receive operations can proceed smoothly as long as the buffer slot is available (with spare capacity). Otherwise, the side of the operation (such as write) is also suspended until the opposite operation (such as receive) occurs.

Code sample

// Two functions are defined here, which verify the effect of synchronous and asynchronous execution respectively
func service(a) {
	time.Sleep(time.Millisecond * 30)
	return "Done"
}
func otherTask(a) {
	fmt.Println("this is other task B")
	time.Sleep(time.Millisecond * 100)
	fmt.Println("Task B is done")}Copy the code

Synchronous mode execution

RetCh := make(chan string) gofunc () {
		ret := service()
		fmt.Println("service return result")
		retCh <- ret 
		fmt.Println("service exited")
	}()
	returnFunc TestAsynService(t * testing.t) {retCh := AsyncService() otherTask() ftt.println (< -retch) time.Sleep(time.Second * 1) }Copy the code

The single test results run as follows. It can be seen that the coroutine will continue to execute until the otherTask finishes and starts to fetch data from chan. It has been suspended until then

this is other task B
service return result
Task B is done
Done
service exited
Copy the code

Asynchronous mode execution

Func AsyncService() chan string {retCh := make(chan string,1) //func () {
		ret := service()
		fmt.Println("service return result")
		retCh <- ret 
		fmt.Println("service exited")
	}()
	return retCh
}

func TestAsynService(t *testing.T) {
	retCh := AsyncService()
	otherTask()
	fmt.Println(<-retCh)
	time.Sleep(time.Second * 1)
}
Copy the code

The result is as follows, and you can clearly see that this mode does not wait for messages from chan and continues directly down

this is other task
service return result
service exited
Task B is done
Done
Copy the code

The channel operations

1. The send operation

c := make(chan int)
c <- 3
Copy the code

Note that continuing to send data to a channel that has already been closed can cause a run-time panic

2. Recive operation

c := make(chan int)
c <- 3
i := <-c
fmt.Println(i) //3
Copy the code

Receiving data from a nil channel is blocked until data is available to receive; Receiving data from a closed channel is not blocked, but immediately returns the zero value of the element type and a bool representing the current state of the channel. You can use this feature to determine whether a channel is closed

ifx, ok := <-ch; Ok {//ok is bool,trueIndicates normal reception,falseChannel closed... }else{... }Copy the code

3. The close operation

c := make(chan int)
close(c)
Copy the code

All channel receivers will immediately return from the blocked wait when a channel is closed with the ok value false(still normal if any value is available). This broadcasting mechanism is often used to send signals simultaneously to multiple subscribers

Code sample

// dataProducer func dataProducer(ch chan int, wg * sync.waitgroup) {gofunc() {
		fori := 0; i < 10; I ++ {ch < -i} close(ch) //channel close wg.done ()}()} // dataReceiver func dataReceiver(ch chan int, wg * sync.waitgroup) {gofunc() {
		for {
			ifdata, ok := <-ch; Ok {// When channel is closed, the ok value becomesfalse
				fmt.Println(data)
			} else {
				break
			}
		}
		wg.Done()
	}()

}

func TestCloseChannel(t *testing.T) {
	var wg sync.WaitGroup
	ch := make(chan int)
	wg.Add(1)
	dataProducer(ch, &wg)
	wg.Add(1)
	dataReceiver(ch, &wg)
	wg.Wait()
Copy the code

Route selection is implemented with switch-case

The select-case statement and channel can realize multi-way selection and timeout control. Each case is followed by a blocking event. When an event receives a response, the wait ends

// Multi-channel selection // The principle is as follows, use select-case statement eachcaseDefault func TestSwitch(t * test.t){select{func TestSwitch(t * test.t){select{func TestSwitch(t * test.t){select{func TestSwitch(t * test.t){case ret1 := <-retCH1:
			t.Logf("case 1 return")
		case ret2 := <-retCH2:
			t.Logf("case 2 return")
		default:
			t.Logf("no one return"Func TestTimeOut(t * testing.t){select {case ret := <- retCH1:
		t.Logf("case 1 return")
	case <-time.After(time.Second*1):
		t.Logf("time out")}}Copy the code