go language learning notes - Advanced - Concurrent Programming: channels - various channels

Unidirectional channel

When declaring a channel, we can set send only or receive only. This channel constrained by the operation direction is called unidirectional channel.

  • Declare a one-way channel

Send only: Chan < -, receive only: < - Chan

var Channel instance chan<- Element type  // Send data only

var Channel instance <-chan Element type  // Accept data only

Channel instance is channel variable; The element type is the data type transmitted by the channel.

  • One way channel usage example

The channel type that can only send data is Chan<-

ch := make(chan int)  // Create a channel instance

var chSendOnly chan<- int = ch // Send only data and assign ch

The channel type that can only receive data is < - Chan

ch := make(chan int)  // Create a channel instance

var chRecvOnly <-chan int = ch // Accept only data and assign ch

Use make to create channels that send data only or read-only data

ch := make(<-chan int)

var chReadOnly <-chan int = ch
<-chReadOnly

A channel that cannot fill data (send data) is meaningless.


Buffered channel

  • Create buffered channel
Channel instance := make(chan Channel element type, Buffer size)

Channel element type: Specifies the type of channel transport element
Buffer size: Specifies the maximum number of elements that can be saved by the channel
Channel instance: created channel variable

package main

import (
	"fmt"
)

func main() {
	ch := make(chan int, 3) // Create a channel with 3 integer element buffer sizes

	fmt.Println(len(ch))   // View current channel size, 0

	ch <- 1 // Send three integer elements to the channel. Because the buffer channel is used, the sender will not block even if no goroutine receives data
	ch <- 2
	ch <- 3

	fmt.Println(len(ch))  // View current channel size, 3
}
  • Blocking condition

Unbuffered channels can be regarded as buffered channels with a length of always 0. The buffered channel will still be blocked in the following cases: (1) when the buffered channel capacity is full, it will be blocked when trying to send data again; (2) When the buffered channel capacity is empty, it will block when trying to receive data.

  • Buffered channel limit buffer size

We know that the channel is a communication bridge between two goroutines. The code using goroutine must have one party's production data and the other party's consumption data. When the producer provides data faster than the consumer consumes data, if the channel does not limit the buffer size, the memory will continue to expand until the application crashes.

Therefore, limiting the buffer size of the channel is conducive to restricting the supply speed of data producers. Data can be processed normally only within the range of data consumer processing capacity + channel size.


Channel multiplexing

Multiplexing usually refers to the process and technology of transmitting multiple signals or data streams on one channel.

The telephone can only receive or send one-way communication at the same time. Telephone and network cable are multiplexed two-way communication modes.

  • The general mode receives data from multiple channels at the same time
for {
    data, ok := <- ch1   // Attempt to receive channel 1
    
    data, ok := <- ch2   // Attempt to receive channel 2
    ...                  // Receive other channels
}
  • Use the select statement to receive data from multiple channels at the same time

go provides the select keyword and responds to the operations of multiple channels at the same time. Each case corresponds to the sending and receiving process of a channel. When the sending and receiving is completed, the response statement in the case will be triggered. Select a case from a select to respond.

select {
  case Operation 1:
      Response action 1
  case Operation 2:
      Response action 2
  ...
  default:
      When there is no operation
}

Operations 1 and 2 are statements related to channel transceiver.

case statement examplesignificance
case <-ch:Receive arbitrary data
case d :=<-ch:Receive channel variable
case ch<-100:send data

When a related operation occurs, the corresponding case response operation will be executed. When no operation corresponding to case occurs, the statement in default is executed by default.


Continue to use after closing the channel

A channel is a reference object, just like a map. When the reference object does not have any external references, the go runtime will automatically garbage collect (GC) memory. Similarly, the channel can also be actively closed.

  • format

Use the close() function to close a channel. Closed channels are still accessible, but there will be some problems.

close(ch)
  • Sending data to closed channels will trigger panic

The closed channel will not be set to nil. If data is sent to the closed channel, panic downtime will be triggered.

package main

import "fmt"

func main() {
    ch := make(chan int)
    
    close(ch)
    
    fmt.Printf("ptr:%p cap:%d len:%d\n", ch, cap(ch), len(ch))
    
    ch <- 1
}
  • When receiving data from a closed channel, there will be no blocking
package main

import (
	"fmt"
	"testing"
)

func main() {
	ch := make(chan int, 2) // Create a channel that can hold two integer elements with a buffer size of 2

	// Put two data in the channel, and the channel is full
	ch <- 0
	ch <- 1

	// Close the channel. At this time, the data with buffered channel will not be released and the channel does not disappear
	close(ch)

	// Traverse all buffered data, and traverse one more
	for i := 0; i < cap(ch)+1; i++ { // The cap obtains the capacity of the channel; One more element is deliberately taken here, resulting in cross-border access
		v, ok := <-ch // Fetch data from channel

		fmt.Println(v, ok) // Print out data status
	}
}

/*
0 true
1 true
0 false
*/

Operation result: the first two lines can normally output the data of the buffered channel, indicating that the data in the channel can still be accessed after the buffered channel is closed.

In the third line, "0 false" indicates the data taken out after the channel is closed. 0 indicates the default zero value of the channel int element, and false indicates that it is not successful because the channel is empty at this time. We can find that after the channel is closed, even if there is no data, the extraction operation will not be blocked, but the extracted data is not normally transmitted.

Tags: Go

Posted on Sun, 24 Oct 2021 23:08:50 -0400 by Smeep