头图

Parallel and concurrent

The difference between parallel and concurrency:

  • Parallel: Two or more programs are executed same time
  • Concurrency: Two or more programs are same time period of .

executed in parallel, at the same time, it is true that multiple programs are executed on the CPU, which requires the CPU to provide multi-core computing capabilities. The , which is executed concurrently by 16119d4bfd6dfb, only observes that there are multiple programs executing on the CPU from a macro point of view, and the microscopic view is that they are executed in rapid rotation on the CPU.

For processes, threads, coroutines, concurrent, parallel, before my article speaks concurrency control when there are introduced, interested can look past one. The portal is here Go pass 09: Concurrency control, goroutine and channel declaration and use!

Go's MPG threading model

The reason why Go is considered a high-performance development language is that it supports coroutine concurrent in the original ecology. A coroutine is a user thread, a lightweight thread. The scheduling of the coroutine depends entirely on the code control of the user space.

The coroutine has its own register context and stack, which are stored in the user space. The coroutine does not need to switch to the kernel mode to access the kernel space when switching, and the switching speed is extremely fast. Developers need to deal with technical issues such as saving and restoring the context information when the coroutine is switched, and managing the size of the stack space in the user space.

Go language uses a special two-level threading model, namely the MPG threading model:

  • M, namely machine, is equivalent to the mapping of kernel threads in the Go process. It corresponds to the kernel threads one-to-one and represents the resources that actually perform calculations. During the life cycle of M, it will only be associated with one kernel thread.
  • P, the processor, represents the context required for the execution of the Go code fragment. The combination of M and P can provide an effective operating environment for G. The combination relationship between them is not fixed. The maximum number of P determines the concurrency scale of the Go program, which is determined by the runtime.GOMAXPROCS variable.
  • G, or goroutine, is a lightweight user thread that encapsulates code fragments and has information such as the stack, state, and code fragments during execution.

In the actual execution process, multiple executable Gs will be sequentially mounted under the executable G queue of P, waiting for scheduling and pointing. When there are some I/O system calls in G that block M, P will disconnect M, get an M from the idle M queue of the scheduler or create a new M for combined execution, so as to ensure that G can be executed in P The other Gs in the queue are executed. Since the number of M executed in parallel in the program has not changed, the CPU utilization of the program is very high.

1. select multiplexing

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

2. Context

When you need to pass context information in multiple goroutines, you can use the Context implementation. In addition to passing contextual information, Context can also be used to pass signals that terminate the execution of subtasks, and suspend multiple goroutines that execute subtasks. The following interfaces are provided in Context:

type Context interface {
    //返回 Context 被取消的时间,即完成工作的截止日期
    Deadline() (deadline time.Time, ok bool)
    
    //1.返回一个 channel,这个 channel 会在当前工作完成或者上下文被取消之后关闭
    //2.多次调用 Done 方法会返回同一个 channel;
    Done() <-chan struct{}
    
    //1.返回 Context 结束的原因,只会在 Done 返回的 channel 被关闭时才会返回非空的值
    //2.如果 Context 被取消,会返回 Canceled 错误
    //3.如果 Context 超时,会返回 DeadlineExceeded 错误
    Err() error
    
    //用于从 Context 中获取传递的键值信息
    Value(key interface{}) interface{}

}

In actual work, a web request may need to start multiple goroutines to work together, and goroutines may need to share the requested information, and when the request is cancelled or the execution times out, all goroutines started by the request need to be terminated and resources are released. At this time, you need to use Context to solve these problems.

Example:

package main

import (
    "context"
    "fmt"
    "time"
)

const DB_ADDRESS  = "db_address"
const CALCULATE_VALUE  = "calculate_value"

func readDB(ctx context.Context, cost time.Duration)  {
    fmt.Println("db address is", ctx.Value(DB_ADDRESS))
    select {
    case <- time.After(cost): //  模拟数据库读取
        fmt.Println("read data from db")
    case <-ctx.Done():
        fmt.Println(ctx.Err()) // 任务取消的原因

        // 一些清理工作

    }

}

func calculate(ctx context.Context, cost time.Duration)  {
    fmt.Println("calculate value is", ctx.Value(CALCULATE_VALUE))
    select {
    case <- time.After(cost): //  模拟数据计算
        fmt.Println("calculate finish")
    case <-ctx.Done():
        fmt.Println(ctx.Err()) // 任务取消的原因
    
        // 一些清理工作

    }

}

func main()  {
    ctx := context.Background(); // 创建一个空的上下文
    // 添加上下文信息
    ctx = context.WithValue(ctx, DB_ADDRESS, "localhost:10086")
    ctx = context.WithValue(ctx, CALCULATE_VALUE, 1234)

    // 设定子 Context 2s 后执行超时返回
    ctx, cancel := context.WithTimeout(ctx, time.Second * 2)

    defer cancel()

    // 设定执行时间为 4 s
    go readDB(ctx, time.Second * 4)
    go calculate(ctx, time.Second * 4)

    // 充分执行
    time.Sleep(time.Second * 5)
}

operation result:

calculate value is 1234
db address is localhost:10086
context deadline exceeded
context deadline exceeded

In the example, we simulate the operation of simultaneous database access and logical calculation in a request. When the request execution times out, the goroutine that has not yet been executed is closed.

  1. First, add context information to the context through the context.WithValue method. Context is concurrent and safe in multiple goroutines.
  2. Then use the context.WithTimeout method to set the timeout time of the Context to 2s, and pass it to the readDB and calculate goroutines to perform subtasks.
  3. In the readDB and calculate methods, use the select statement to monitor the Done channel of the Context. Since we set that the child context will time out after 2s, it will close the Done channel after 2s; however, the default execution time of the subtask is 4s, the corresponding case statement has not yet returned, the execution is cancelled, and it enters the cleanup work. In the case statement, the task performed by the current goroutine is ended.

微客鸟窝
37 声望3 粉丝