What are processes and threads
A process is an application’s workspace. For example, when you open QQ or wechat, the workspace contains all the resources that the application needs to run. Threads are the unit of execution in a process, with at least one thread per process.
Process versus thread
- A process is the smallest unit of system resource allocation and scheduling
- A thread is the smallest unit of program execution
- A process consists of one or more threads, which are different execution paths for code in the process
- The processes are independent of each other, and the threads in the process share the memory space and resources of the program
- Threads are more efficient in both time and space than processes
coroutines
Coroutines are lightweight threads in user mode. Threads are scheduled by the CPU, and the scheduling of coroutines is completely controlled by the user.
Coroutines versus threads
- A thread can have multiple coroutines
- Threads and processes are synchronous mechanisms, while coroutines are asynchronous
- Coroutines can retain the state of the last call, and when the procedure is reentered, it is equivalent to entering the state of the last call
- Coroutines need threads to run, so they don’t replace threads, threads are partitioned CPU resources, and coroutines are organized code flows
Concurrent, parallel
- Concurrency and parallelism are relative to processes or threads.
- Concurrency is the multiplexing of one or more cpus to multiple processes/threads. In layman’s terms, the cpus take turns performing multiple tasks, with each task performing a small segment, which in a macro sense acts as if they were executing simultaneously.
- Parallelism must be supported by multiple cpus, literally executing multiple processes or threads at the same time.
Go language coroutine
There are no threads in Go, only goroutines, which are lighter and context-switching faster than threads. Goroutine is scheduled by Go itself, we just enable it. Goroutine is started with the go keyword, very simple, followed by a method or function go function()
package main
import (
"fmt"
"time"
)
func main (a){
go fmt.Println("Tiny Guest Nest")
fmt.Println("I am clean.")
time.Sleep(time.Second) // Wait one second for the goroutine to complete
}
Copy the code
Running results:
I am a dust-free bird's nestCopy the code
Channel
Channels are used to solve communication problems between multiple Goroutines.
In the Go language, communication is encouraged to share memory through communication, rather than through shared memory communication. In fact, it is advocated to transfer data through channel sending and receiving messages, rather than by modifying the same variable. Therefore, channel is preferred in data flow and transfer scenarios because it is concurrency safe and has good performance.
The channel statement
ch := make(chan string)
- Using make
- Chan is the keyword, representing the channel type, and chan is a collection type
- String represents the type of data stored in a channel
Chan, the use of
Chan has only two operations: send and receive:
- Send: <-chan // Send data to chan
- Receive: chan-> // Get data from chan
Example:
package main
import (
"fmt"
)
func main(a) {
ch := make(chan string)
go func(a){
fmt.Println("Tiny Guest Nest")
ch <- "Executed"
}()
fmt.Println("I am clean.")
value := <-ch
fmt.Println("The value of chan obtained is:",value)
}
Copy the code
The results
I got it from the bird's nestchanIs: No further action is requiredCopy the code
With chan, we don’t need to use Sleep to wait for the Goroutine to finish, because the receive operation (value := <-ch) blocks until it gets a value.
There is no buffer channel
The above operation is an unbuffered channel. The capacity of the channel is 0, it cannot store data, but only plays the role of transmission, so the sending and receiving operations of an unbuffered channel are carried out simultaneously
Have a buffer channel
We can create a buffered channel by passing in a second parameter, the channel capacity, when we declare it.
// Create a channel of capacity 3 that can hold three elements of type int
ch := make(chan int.3)
Copy the code
- There is a queue inside a buffered channel
- The send operation appends elements to the end of the queue and, if the queue is full, blocks until the receive operation removes the element from the queue.
- The receive operation takes the element from the head of the queue and, if the queue is empty, blocks until the send operation appends the element to the queue.
- You can use built-in functions
cap
The built-in function len is used to get the number of elements in a channel.
ch := make(chan int.3)
ch <- 1
ch <- 2
fmt.Println("Capacity is".cap(ch),"The number of elements is:.len(ch))
// Print the result: the capacity is 3 and the number of elements is 2
Copy the code
Shut down the channel
Use the built-in function close :close(ch)
- If a channel is closed, data cannot be sent to it; otherwise, a Panic exception will occur.
- You can receive data from a closed channel, or if there is no data, you receive a zero value of the element type.
A one-way channel
A channel that can only be sent or received is a one-way channel.
Unidirectional channel declaration
Just add the operator to the base declaration:
send := make(ch<- int) // Can only send data to channels
receive := make(<-ch int) // Can only receive data from channels
Copy the code
Example:
package main
import (
"fmt"
)
// Only channels can be sent
func send(s chan<- string){
s <- "Tiny Guest Nest"
}
// Can only receive channels
func receive(r <-chan string){
str := <-r
fmt.Println("str:",str)
}
func main(a) {
// Create a two-way channel
ch := make(chan string)
go send(ch)
receive(ch)
}
// Result: STR: micro guest nest
Copy the code
select+channel
Select enables multiplexing, that is, listening for multiple channels at the same time.
- If you find which channel has data generated, execute the corresponding case branch
- If more than one case branch can be executed at the same time, one is randomly selected
- If none of the case branches can be executed, the select waits
Example:
package main
import (
"fmt"
)
func main(a) {
ch := make(chan int.1)
for i := 0; i < 10; i++ {
select {
case x := <-ch:
fmt.Println(x)
case ch <- i:
fmt.Println("--", i)
}
}
}
Copy the code
Running results:
-- 0
0
-- 2
2
-- 4
4
-- 6
6
-- 8
8
Copy the code
So I’m going to leave that as a question, why is it output like this?