头图

What are processes and threads

A process is the workspace of an application, such as QQ and WeChat you open. The workspace contains all the resources needed for the program to run. The thread is the execution unit in the process, and a process has at least one thread.

Process and thread comparison

  • process is the smallest unit of system resource allocation and scheduling
  • Thread is the smallest unit of program execution
  • A process consists of one or more threads, which are different execution routes of the 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 time and space than processes

Coroutine

A coroutine is a lightweight thread in a user mode. The thread is scheduled by the CPU, and the scheduling of the coroutine is completely controlled by the user.

Coroutine and thread comparison

  • A thread can have multiple coroutines
  • Threads and processes are synchronous mechanisms, while coroutines are asynchronous
  • The coroutine can retain the state of the last call. When the process is reentered, it is equivalent to entering the state of the last call
  • need threads to carry and run, so coroutines cannot replace threads. 160fa29b27933b threads are divided CPU resources, and coroutines are organized code flows

Concurrency, parallel

  1. Concurrency and parallelism are relative to processes or threads.
  2. Concurrency is the multiplexing between one or more CPUs and multiple processes/threads. In layman's terms, the CPU performs multiple tasks in turn, and each task executes a short period of time. From a macro point of view, it is like executing at the same time.
  3. Parallel must have multiple CPUs to provide support, in the true sense of executing multiple processes or threads at the same time.

Go language coroutine

There is no concept of threads in Go, only goroutines, which are lighter and faster in context switching than threads. Goroutine is scheduled by Go itself, we just enable it.
The goroutine is by the 160fa29b279458 go keyword, which is very simple. Add a method or function go function()

package main

import (
    "fmt"
    "time"
)

func main (){
    go fmt.Println("微客鸟窝")
    fmt.Println("我是无尘啊")
    time.Sleep(time.Second) //等待一秒,使goroutine 执行完毕
}

operation result:

我是无尘啊
微客鸟窝

Channel

Channel (channel) is used to solve the communication problem between multiple goroutines.

In the Go language, it is advocated to share memory through communication instead of communicating through shared memory. In fact, it is to promote data transfer by sending and receiving messages through channels instead of modifying the same variable. Therefore, channels should be used first in data flow and transmission scenarios. It is concurrency safe and has good performance.

channel declaration

ch := make(chan string)

  • Use the make function
  • chan is a keyword, which means channel type, chan is a collection type
  • string indicates the type of data stored in the channel

chan use

chan has only two operations: sending and receiving:

  1. Send: <-chan //Send data to chan
  2. Receive: chan-> //Get data from chan

Example:

package main

import (
    "fmt"
)

func main() {
    ch := make(chan string)
    go func(){
        fmt.Println("微客鸟窝")
        ch <- "执行完毕"
    }()

    fmt.Println("我是无尘啊")
    value := <-ch
    fmt.Println("获取的chan的值为:",value)
}

operation result

我是无尘啊
微客鸟窝
获取的chan的值为: 执行完毕
With chan, we can wait for the goroutine to finish executing without using Sleep, because the receiving operation ( value := <-ch ) will block and wait until it gets the value.

Unbuffered channel

The above operation is an unbuffered channel, the capacity of the channel is 0, it can not store data, but plays a role in transmission, so the sending and receiving operations of the unbuffered channel are carried out at the same time

Buffered channel

When declaring, we can pass in the second parameter, that is, the channel capacity size , so that a buffered channel is created.

//创建一个容量为3的 channel,其内部可以存放3个类型为int的元素
ch := make(chan int,3)
  • There is a queue inside the buffered channel
  • The sending operation is to append an element to the end of the queue. If the queue is full, it will block and wait until the receiving operation removes the element from the queue.
  • The receiving operation is to remove the element from the head of the queue. If the queue is empty, it will block and wait until the sending operation adds an element to the queue.
  • The capacity of the channel can be obtained through the built-in function cap , and the number of elements in the channel can be obtained through the built-in function len.
ch := make(chan int,3)
ch <- 1
ch <- 2
fmt.Println("容量为",cap(ch),"元素个数为:",len(ch))
//打印结果:容量为 3 元素个数为: 2

Close channel

Use built-in function close: close(ch)

  • If the channel is closed, you can no longer send data to it, otherwise it will cause a panic exception.
  • Data can be received from the closed channel. If there is no data, the zero value of the element type is received.

One-way channel

The channel that can only send or can only receive is a one-way channel.

One-way channel declaration

You only need to add operators to the basic declaration:

send := make(ch<- int) //只能发送数据给channel
receive := make(<-ch int) //只能从channel中接收数据

Example:

package main

import (
    "fmt"
)
//只能发送通道
func send(s chan<- string){
    s <- "微客鸟窝"
}
//只能接收通道
func receive(r <-chan string){
    str := <-r
    fmt.Println("str:",str)
}
func main() {
    //创建一个双向通道
    ch := make(chan string)
    go send(ch)
    receive(ch)
}

//运行结果: str: 微客鸟窝

select+channel

select can achieve multiplexing, that is, monitor multiple channels at the same time.

  • When finding which channel has data generated, execute the corresponding case branch
  • If there are multiple case branches that can be executed at the same time, one will be randomly selected
  • If a case branch is not executable, select will wait forever

Example:

package main

import (
    "fmt"
)

func main() {
    ch := make(chan int, 1)
    for i := 0; i < 10; i++ {
        select {
        case x := <-ch:
            fmt.Println(x)
        case ch <- i:
            fmt.Println("--", i)
        }
    }
}

operation result:

-- 0
0
-- 2
2
-- 4
4
-- 6
6
-- 8
8

This is left as a question, why is it output like this?


微客鸟窝
37 声望3 粉丝