What is a process, a thread

A process is the workspace of an application program. For example, if you open QQ or WeChat, the workspace contains all the resources that the program needs to run. A thread is a unit of execution in a process, with at least one thread per process.

Process versus thread

  • 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 of code in the process
  • Processes are independent of each other, and threads in the process share the program’s memory space and resources
  • Threads are more efficient in both time and space than processes

coroutines

Coroutines are user-oriented lightweight threads. Threads are scheduled by the CPU, and the scheduling of coroutines is completely controlled by the user.

Contrast coroutines with threads

  • 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, and when the procedure reenters, it is equivalent to entering the state of the last call
  • Coroutines need threads to run, so they cannot replace threads. Threads are CPU resources that are divided, and coroutines are organized code flows

Concurrency, parallelism

  1. Concurrency and parallelism are relative to processes or threads.
  2. Concurrency is multiplexing between one or more CPUs to multiple processes/threads. In general, the CPUs execute multiple tasks in turn, and each task executes for a short period of time, as if it were simultaneously executed from a macro perspective.
  3. Parallel must be supported by multiple CPUs, in the true sense that multiple processes or threads are executing at the same time.

Go language coroutines

There is no concept of threads in Go, only goroutines, which are lighter and faster for context switching than threads. Goroutines are scheduled by Go itself, and we just enable them. The goroutine is started with the go keyword, which is very simple, followed by a method or function, go function().

Package main import (" FMT ""time") func main (){go FMT.Println(" I'm clean ") FMT.Println(" I'm clean ") time.sleep (time.second) // wait one second for goroutine to complete}

Running results:

I am clean ah micro guest bird's nest

Channel

Channels are used to solve the problem of communicating between multiple goroutines.

In the Go language, the idea of sharing memory through communication, rather than through shared memory, is to send and receive messages through a channel, rather than by modifying the same variable. Therefore, channel is preferred in data flow and transfer scenarios. It is concurrency safe and has good performance.

The channel statement

ch := make(chan string)

  • Using the make function
  • Chan is a keyword that represents the type of Channel, and Chan is a collection type
  • A string represents the type of data in a channel

Chan, the use of

Chan only has two operations: send and receive:

  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 (" Chan String ") ch <- "Finch"}() FMT.Println(" Chan is clean ") value := <-ch FMT.Println(" Chan is clean ",value)}

The results

I got the value of CHAN from Dustless Bird Nest: Execution completed

With chan, we can wait for goroutine to finish without Sleep, because the receive operation (
value := <-ch) blocks the wait until it gets the value.

There is no buffer channel

An unbuffered channel is an unbuffered channel with a capacity of zero. It does not store data, but only transmits data. Therefore, an unbuffered channel sends and receives data simultaneously

Have a buffer channel

When we declare it, we can pass in a second parameter, the size of the channel, thus creating a buffered channel.

Ch := make(chan int,3) ch := make(chan int,3)
  • There’s a buffer and there’s a queue inside a channel
  • The send operation appends elements to the end of the queue, and if the queue is full, it blocks and waits until the receive operation takes the element from the queue.
  • The receive operation takes an element from the head of the queue and, if the queue is empty, blocks waiting until the send operation appends an element to the queue.
  • This can be done through built-in functionscapTo get the capacity of a channel, the built-in function len gets the number of elements in the channel.
Ch := make(chan int,3) ch < -1 ch < -2 fmt.Println(" c ",cap(ch), len(ch)) // Println(" c ",len(ch)

Shut down the channel

Use the built-in function close :close(ch)

  • If a channel is closed, no data can be sent to it, otherwise a panic exception will be raised.
  • You can receive data from a closed channel, or if there is no data, you will receive a zero value of the element type.

A one-way channel

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

One-way channel declaration

Simply add the operator to the underlying declaration:

Send := make(ch< -int) // only send data to channel receive := make(<-ch int) // only receive data from channel

Example:

Package main import (" FMT ") // Can only send channel func send(s Chan < -string){s <- "micro guest"} // Can only receive channel func receive(r <-chan string){ STR := <-r FMT.Println(" STR :", STR)} func main() {ch := make(chan string) go send(ch) receive(ch)} STR: Micro Guest Nest

select+channel

SELECT can be multiplexed, that is, listening to multiple channels at the same time.

  • If a channel is found to produce data, the corresponding case branch is executed
  • If more than one case branch can be executed at the same time, one will be selected at random
  • If none of the case branches are executable, the SELECT will wait

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)
        }
    }
}

Running results:

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

This is a question for thinking, why would this output?