"Under a second" Do you understand goroutine and channel?

The open source library "go home" focuses on the Go language technology stack and interview questions to help Gopher get on the bigger stage. Welcome go home~

Background introduction

Everyone knows that a process is the basic unit of operating system resource allocation. It has an independent memory space. Threads can share the memory space of the same process. Therefore, threads are relatively lightweight and the context switching overhead is also small. Although the thread is already lightweight, it still occupies nearly 1M of memory. The Goroutine, known as "lightweight thread" introduced today, can be as small as tens of K or even a few K, and the switching overhead is smaller.

In addition, in traditional Socket programming, you need to maintain a thread pool to allocate threads for each Socket sending and receiving packets, and you need to establish a correspondence between the CPU and the number of threads to ensure that each task can be allocated to the CPU in time, and The Go program can intelligently distribute the tasks in the goroutine to the CPU.

how to use

We now assume a scenario in which you are a company manager who spends two hours a day processing mail and six hours meeting, then the program can be written like this.

func main() {

	time.Sleep(time.Hour * 2) //处理邮件
	time.Sleep(time.Hour * 6) //开会

	fmt.Println("工作完成了")
}

Run it

file

Sure enough, it takes 8 hours to complete the work, so how to simplify the work? That's right, please ask an assistant sister to help, let her handle the mail, so that you only need to meet for 6 hours.

file

Let's start writing code, let's define an assistant function first.

func assistant() {
	time.Sleep(time.Hour * 2)
}

Then gocall it with an artifact command in the main function , so that the assistant takes a long time and no longer takes up your time.

func main() {

	go assistant()

	time.Sleep(time.Hour * 6) //开会

	fmt.Println("工作完成了")
}

Run it

file

It really took only six hours to finish the work. Dear judges, see? No, this is a coroutine, just goadd the function name to the command, it's that simple.

file

But I know you will not be content with the status quo.

Anonymous function

In addition, since goroutine supports ordinary functions, of course it also supports anonymous functions.

go func() {
  time.Sleep(time.Hour * 2)
}()

How to communicate between coroutines

Although we can easily create a bunch of coroutines, coroutines that cannot communicate have no soul. If the assistant is helping you deal with mail, and you suddenly want to ask her to drink milk tea, do you want to notify her?

file

How do you notify? This leads to the famous channel, the Chinese translation of "channel", as the name implies, its role is to establish a channel between coroutines, one end can continuously transmit data sources to the other end of the channel.

file

The declaration method is also very simple, just make it. Take the following code as an example, it represents initializing a channel type variable, and only string type data can be stored in the channel.

ch := make(chan string)

After the initialization is complete, to establish a connection with the coroutine function, you must first pass the chan variable to the coroutine function.

go assistant(ch)

Of course, the coroutine function needs to be able to receive chan. Let's go deep inside the function and see what we have done.

func assistant(ch chan string) {

	go func() {
		for {
			fmt.Println("看了一封邮件")
			time.Sleep(time.Second * 1)
		}
	}()

	msg := <-ch
	if msg == "喝奶茶去呗" {
		ch <- "好啊"
	}
}

A coroutine has been created inside the function to deal with mails, while waiting for the boss's notification. Attentively, you should see how to get the channel data. Yes, you only need to add the <-symbol in front of the channel variable to get the value. Similarly, adding the symbol to the back is to plug the data into the channel.

ch <- "pingyeaa"
<- ch

file

If the channel has no data, the consumer will block until there is data. Of course, the compiler is very clever. If it finds that there is no place to insert data into the channel during compilation, it will panic and prompt a deadlock.

fatal error: all goroutines are asleep - deadlock!

Continue to look at the code, which roughly means that if the boss sends "Drink milk tea to chant", it returns "OK". Because the channel has no data at the beginning, the coroutine will block until the main function writes to the channel Into the news.

Now let's look at the implementation logic of the main function. The declaration channel and the incoming channel variables will not be repeated. We only need to wait for 5 seconds and write the milk tea message into the channel. Because the assistant coroutine just wrote a "good" message to ch after receiving the message, the main function should read the message from the assistant after sending the request.

ch := make(chan string)

go assistant(ch)

time.Sleep(time.Second * 5)
ch <- "喝奶茶去呗"

resp := <-ch
fmt.Println(resp)

Similarly, the main function <-chwill block until the assistant replies to the message. There are two other points to note. First, if the main function is executed before the goroutine, the goroutine will also be destroyed; second, the main is also a goroutine.

Finally, to close the channel, in fact, it is not necessary to close the channel. It is different from the file. If no channel is used by the goroutine, it will be automatically destroyed. The purpose of close is to notify the other end of the channel that no more messages will be sent, and the other end can Use the second parameter of <-ch to get the channel closing status.

close(ch)

data, ok := <-ch

Multiplexing selection of channels

The <-ch in the previous example can only read one message of the channel. If there is more than one message in the channel, how to read it?

file

Many students should think of traversal as much as I do. That's right, traversal can indeed get channel data.

for {
  fmt.Println(<-ch)
}

It can also be traversed in this way.

for d := range ch {
  fmt.Println(d)
}

But what if you need to receive data from multiple channels simultaneously? Receive two channel variables in the loop?

for {
  data, ok := <-ch1
  data, ok := <-ch2
}

Although data can be taken out in this way, the performance is poor. The select keyword provided by us is specifically used to solve the problem of multi-channel data reading. The syntax is very similar to switch. Select will distribute the data from multiple channels to different processing logic.

func main() {

	ch1 := make(chan int)
	ch2 := make(chan int)

	go func() {
		for {
			select {
			case d := <-ch1:
				fmt.Println("ch1", d)
			case d := <-ch2:
				fmt.Println("ch2", d)
			}
		}
	}()

	ch1 <- 1
	ch1 <- 2
	ch2 <- 2
	ch1 <- 3
}

Simulation timeout

In addition, in some cases, we do not want the channel to block for too long. Assuming that the channel data cannot be retrieved within 5 seconds, we exit with a timeout. Then we can use the time.After method to achieve it. time.After will return a channel type, its role is to pass in a target time (such as 5s), we can get the preset timeout notification through the channel after 5 seconds, so as to achieve the purpose of the timer.

func main() {

	ch1 := make(chan int)
	ch2 := make(chan int)

	go func() {
		for {
			select {
			case d := <-ch1:
				fmt.Println("ch1", d)
			case d := <-ch2:
				fmt.Println("ch2", d)
			case <-time.After(time.Second * 5):
				fmt.Println("接收超时")
			}
		}
	}()

	time.Sleep(time.Second * 6)
}

Channel closed extended reading

Panic will be triggered if the channel is closed again

ch := make(chan int)
close(ch)
ch <- 1
panic: send on closed channel

Channel setting length

You can set the channel length by the make method. As a buffer, the producer side will block when the channel is full, and the consumer side will block when the channel is empty.

ch := make(chan int, 3)

ch <- 1
ch <- 2
ch <- 2
ch <- 2

fmt.Println(len(ch))

Closed channels can still read data

ch := make(chan int, 3)

ch <- 1
ch <- 2
ch <- 2

close(ch)

for d := range ch {
  fmt.Println(d)
}

Thank you for watching. If you think the article is helpful to you, welcome to pay attention to the public account "Pingya" and focus on Go language and technical principles.
follow me

Guess you like

Origin www.cnblogs.com/pingyeaa/p/12699543.html