Concurrent cases
1, Unbuffered: producer consumer
1.1 cases
package main import ( "fmt" "time" ) func Producer(p chan<- int) { for i := 0; i < 10; i++ { p <- i fmt.Println("producer-->commodity:", i) } } func Consumer(c <-chan int) { for i := 0; i < 10; i++ { v := <-c fmt.Println("consumer-->Consumer goods:", v) } } func main() { queue := make(chan int) // ch :=make(chan int) // ch <- 1 // This error means that the thread is in a deadlock and the program cannot continue to execute. So what is the reason for this mistake? // We create an unbuffered channel and assign a value to the channel. The program falls into a deadlock after the assignment is completed. // Because our channel is unbuffered, i.e. synchronous, the program is blocked before we can read the channel after the assignment is completed. // queue <- 1 // Queue < - 2 / / full blocking // Open the co process execution function to execute separately, and the main program will execute directly down go Producer(queue) // When i = 0, the write is blocked, the producer and consumer execute asynchronously, and the consumer will read the channel value. go Consumer(queue) // Let Producer and Consumer complete time.Sleep(1e9) } output consumer-->Consumer goods: 0 producer-->commodity: 0 producer-->commodity: 1 consumer-->Consumer goods: 1 consumer-->Consumer goods: 2 producer-->commodity: 2 producer-->commodity: 3 consumer-->Consumer goods: 3 consumer-->Consumer goods: 4 producer-->commodity: 4 producer-->commodity: 5 consumer-->Consumer goods: 5 consumer-->Consumer goods: 6 producer-->commodity: 6 producer-->commodity: 7 consumer-->Consumer goods: 7 consumer-->Consumer goods: 8 producer-->commodity: 8 producer-->commodity: 9 consumer-->Consumer goods: 9
1.2 description
Because the channel is not buffered, when the producer assigns a value to the channel, the producer thread will block until the consumer thread takes out the data in the channel.
After the consumer takes out the data for the first time, the consumer's thread will also block during the next cycle, because the producer has not stored the data, and the program will execute the producer's thread.
In this way, the program continues to switch between the two threads of consumer and producer until the end of the loop.
2, Buffer: producer consumer
2.1 cases
package main import ( "fmt" "time" ) func Producer(p chan<- int) { for i := 0; i < 10; i++ { p <- i fmt.Println("producer-->commodity:", i) } } func Consumer(c <-chan int) { for i := 0; i < 10; i++ { v := <-c fmt.Println("consumer-->Consumer goods:", v) } } func main() { queue := make(chan int, 10) //Buffered example // Open the co process execution function to execute separately, and the main program will execute directly down go Producer(queue) go Consumer(queue) // Let Producer and Consumer complete time.Sleep(1e9) } output producer-->commodity: 0 producer-->commodity: 1 producer-->commodity: 2 producer-->commodity: 3 consumer-->Consumer goods: 0 consumer-->Consumer goods: 1 consumer-->Consumer goods: 2 consumer-->Consumer goods: 3 consumer-->Consumer goods: 4 producer-->commodity: 4 producer-->commodity: 5 producer-->commodity: 6 producer-->commodity: 7 producer-->commodity: 8 producer-->commodity: 9 consumer-->Consumer goods: 5 consumer-->Consumer goods: 6 consumer-->Consumer goods: 7 consumer-->Consumer goods: 8 consumer-->Consumer goods: 9
2.2 description
In this program, the buffer can store 10 integers of type int. when executing the producer thread, the thread will not block and store 10 integers in the channel at one time.
When reading, it is also a one-time reading. If read, no data will block
3, Randomly write 0 or 1 to the channel
3.1 cases
package main import ( "fmt" "time" ) func main() { ch := make(chan int, 1) for i := 0; i < 10; i++ { ///Keep writing 0 or 1 randomly to the channel select { case ch <- 0: case ch <- 1: } //Fetch data from channel i := <-ch fmt.Println("The data for the pipeline is:", i) time.Sleep(1e9) } } output The data for the pipeline is: 0 The data for the pipeline is: 1 The data for the pipeline is: 1 The data for the pipeline is: 0 The data for the pipeline is: 1 The data for the pipeline is: 0 The data for the pipeline is: 1 The data for the pipeline is: 0 The data for the pipeline is: 0 The data for the pipeline is: 0
4, High performance concurrent programming must set gomaxproc to the maximum number of cores, which is determined by runtime Numcpu() get
When performing some expensive computing tasks, we hope to make full use of the multi-core characteristics of modern servers to parallelize the tasks as much as possible, so as to reduce the total computing time.
At this time, we need to understand the number of CPU cores and decompose the computing tasks into multiple goroutine s to run in parallel.
Let's simulate a completely parallel computing task: calculating the sum of N integer numbers.
4.1. Case: calculate the sum of N integer numbers
We can divide all integer numbers into M parts, M is the number of CPU s.
Let each CPU start to calculate the calculation task assigned to it, and finally accumulate the calculation results of each CPU again, so as to get the sum of all N integer numbers
package main type Vector []float64 // Computing tasks assigned to each CPU func (v Vector) DoSome(i, n int, u Vector, c chan int) { for ; i < n; i++ { v[i] += u[i] } c <- 1 // Signal the task manager that I have finished the calculation } const NCPU = 16 // Assume a total of 16 cores func (v Vector) DoAll(u *Vector) { c := make(chan int, NCPU) // It is used to receive the task completion signal of each CPU for i := 0; i < NCPU; i++ { go v.DoSome(i*len(v)/NCPU, (i+1)*len(v)/NCPU, u, c) } // Wait for all CPU tasks to complete for i := 0; i < NCPU; i++ { <-c // A data is obtained, indicating that a CPU calculation is completed } // This means that all calculations have been completed } func main() { }
DoAll() divides the tasks according to the number of CPU cores, and then opens up multiple goroutine s to execute these computing tasks in parallel.
We can first control how many CPU cores are used by setting the value of the environment variable GOMAXPROCS.
The specific operation method is to directly set the value of the environment variable GOMAXPROCS, or call the following statement before starting goroutine in the code to set the use of 16 CPU cores:
runtime.GOMAXPROCS(16)
How many CPU cores should be set? In fact, another function NumCPU() is provided in the runtime package to obtain the number of cores.
5, Actively transfer the time slice to other goroutines and execute the current goroutine at a certain time in the future
It is implemented using the Gosched() function in the runtime package.