golang USES context to control concurrent application scenarios

  • 2020-08-22 22:07:37
  • OfStack

It is very common to have multiple goroutine scenarios in golang. The two most commonly used methods are WaitGroup and Context. Today we will look at the application scenarios of Context

Usage scenarios

Scenario 1: Multiple goroutine performs timeout notifications

One of the most common cases of concurrent execution is a coroutine execution timeout. If you don't do a timeout, there will be a zombie process. This adds up to a lot of confusion, so avoid them at the source

Look at this example:


package main

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

/**
 with 1 a content Can control multiple goroutine,  Make sure the thread is under control ,  Not every new one 1 a goroutine There must be 1 a chan Go tell him to shut it down 
 The code is much cleaner with him 
*/

func main() {
 fmt.Println("run demo \n\n\n")
 demo()
}

func demo() {
 ctx, cancel := context.WithTimeout(context.Background(), 9*time.Second)
 go watch(ctx, "[ thread 1]")
 go watch(ctx, "[ thread 2]")
 go watch(ctx, "[ thread 3]")

 index := 0
 for {
  index++
  fmt.Printf("%d  Seconds passed  \n", index)
  time.Sleep(1 * time.Second)
  if index > 10 {
   break
  }
 }

 fmt.Println(" Notification to stop monitoring ")
 //  I'm actually running out of time ,  The coroutine has been withdrawn in advance 
 cancel()

 //  Prevents the main process from exiting prematurely 
 time.Sleep(3 * time.Second)
 fmt.Println("done")
}

func watch(ctx context.Context, name string) {
 for {
  select {
  case <-ctx.Done():
   fmt.Printf("%s  Monitor the exit ,  stopped ...\n", name)
   return
  default:
   fmt.Printf("%s goroutine In the monitoring ... \n", name)
   time.Sleep(2 * time.Second)
  }
 }
}

Use context.WithTimeout () to set a time limit for the text stream and combine for+select to receive messages < -ctx.Done () sends a message and all users using the same context receive this notification, eliminating the tedious code of one-by-one notification

Scenario 2: Similar to session on the web server

For example, in php (no swoole extensions), when a request comes in, all you can get from $_REQUEST $_SERVER is information about that request, and even global variables are used to serve that request, which is thread safe

But golang don't 1 sample, because the program itself can be up 1 web sever, so can't literally use global variables, or memory leaks warned. But in the actual business needs to have a similar session thing to carry a single request information, a concrete example is: add 1 to each request uniqueID how to handle the & # 63; With this uniqueID, you can bring it to all the logs requested so that you can track what happened to one request while troubleshooting problems

As follows:


func demo2() {
 pCtx, pCancel := context.WithCancel(context.Background())
 pCtx = context.WithValue(pCtx, "parentKey", "parentVale")
 go watch(pCtx, "[ The parent process 1]")
 go watch(pCtx, "[ The parent process 2]")

 cCtx, cCancel := context.WithCancel(pCtx)
 go watch(cCtx, "[ The child process 1]")
 go watch(cCtx, "[ The child process 2]")
 fmt.Println(pCtx.Value("parentKey"))
 fmt.Println(cCtx.Value("parentKey"))

 time.Sleep(10 * time.Second)
 fmt.Println(" Subprocess closed ")
 cCancel()
 time.Sleep(5 * time.Second)
 fmt.Println(" Parent process closed ")
 pCancel()

 time.Sleep(3 * time.Second)
 fmt.Println("done")
}

At the beginning, context.Background () in ES52en.WithCancel (context.Background ()) is a newly created context. By using the characteristics that context can inherit, one context tree can be built for its own program.

At the same time, ES67en.WithValue will also add custom values to context, so that uniqueID can be passed down easily, instead of passing parameters layer by layer, func or something

For context, the following applications are worthy of reference:

Gin logrus

Context related func and interfaces

Inheriting context requires the following four interfaces


type Context interface {
 Deadline() (deadline time.Time, ok bool)

 Done() <-chan struct{}

 Err() error

 Value(key interface{}) interface{}
}

There is no need to implement the interface when using it, as one is already implemented in the official package based on emptyCtx, and the calling method has


var (
 background = new(emptyCtx)
 todo  = new(emptyCtx)
)

//  This is the initial one ctx,  After the child ctx It's all inherited from it 
func Background() Context {
 return background
}

//  Don't know context Going to do ,  But there has to be 1 a ctx The use of the 
func TODO() Context {
 return todo
}

Inherited functions


func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithValue(parent Context, key, val interface{}) Context
WithCancel returns 1 ctx with cancel function, WithDeadline automatically executes cancel() upon arrival at the specified time WithTimeout is the shell of WithDeadline, the difference being how much time passes before cancel is executed

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
 return WithDeadline(parent, time.Now().Add(timeout))
}

WithValue inherits its parent class ctx with a value


Related articles: