Before Golang's version 1.7, context was in the package /x/net/context, but later I found that it is needed in many places, and all of them were included in Golang's standard library since 1.7. The Context package is specially used to simplify the operations related to the data, cancel signals, deadlines and other related operations between multiple goroutines that handle a single request. So this article will take a look at its usage and implementation principles.
Source code analysis
First, let’s take a look at several core data structures in Context:
Context interface
type Context interface { Deadline() (deadline , ok bool) Done() <-chan struct{} Err() error Value(key interface{}) interface{} }
Deadline returns a time when the current Context should end, ok indicates whether there is a deadline.
The Done method returns a close channel when the Context is cancelled or timed out. The close channel can be used as a broadcast notification to tell the context-related function to stop the current work and then return.
Err method returns why context is cancelled.
Value allows Goroutine to share some data, of course, it is coroutine to obtain data. However, when using these data, you should pay attention to synchronization, such as returning a map, and the reading and writing of this map must be locked.
canceler interface
canceler interface defines the context that provides cancel function:
type canceler interface { cancel(removeFromParent bool, err error) Done() <-chan struct{} }
There are 4 ready-made implementations:
- emptyCtx: empty Context, only implements the Context interface;
- cancelCtx: inherits from Context and implements cancelerinterface
- timerCtx: inherited from cancelCtx, can be used to set timeout;
- valueCtx: can store a pair of key-value pairs;
Inherit Context
The context package provides functions to assist users in creating new Context objects from existing Context objects. These Context objects form a tree: When a Context object is cancelled, all Contexts inherited from it are cancelled.
Background is the root of all Context object trees, it cannot be cancelled, it is an instance of emptyCtx:
var ( background = new(emptyCtx) ) func Background() Context { return background }
The main method to generate a Context
WithCancel
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { c := newCancelCtx(parent) propagateCancel(parent, &c) return &c, func() { (true, Canceled) } }
Returns a cancelCtx example and returns a function that can directly call() on the outer layer to cancel the Context.
WithDeadline
func WithDeadline(parent Context, deadline ) (Context, CancelFunc) { if cur, ok := (); ok && (deadline) { return WithCancel(parent) } c := &timerCtx{ cancelCtx: newCancelCtx(parent), deadline: deadline, } propagateCancel(parent, c) d := (deadline) if d <= 0 { (true, DeadlineExceeded) // deadline has already passed return c, func() { (true, Canceled) } } () defer () if == nil { = (d, func() { (true, DeadlineExceeded) }) } return c, func() { (true, Canceled) } }
Returns a timerCtx example, sets the specific deadline time, and when the deadline reaches the deadline, the descendant goroutine exits.
WithTimeout
func WithTimeout(parent Context, timeout ) (Context, CancelFunc) { return WithDeadline(parent, ().Add(timeout)) }
Like WithDeadline, it returns a timerCtx example. In fact, WithDeadline packages a layer and directly passes in the duration of the time, exits after the end.
WithValue
func WithValue(parent Context, key, val interface{}) Context { if key == nil { panic("nil key") } if !(key).Comparable() { panic("key is not comparable") } return &valueCtx{parent, key, val} }
WithValue corresponds to valueCtx. WithValue is to set a map in the Context, and the goroutine of this Context and its descendants can get the value in the map.
example
The most commonly used place in Context is in Golang's web development. In the server of the http package, each request has a corresponding goroutine to process. Request handling functions usually start additional goroutines to access backend services, such as databases and RPC services. The goroutine used to process a request usually requires access to some data specific to the request, such as the end user's identity authentication information, verification-related tokens, and the request deadline. When a request is cancelled or timed out, all goroutines used to process the request should exit quickly before the system can free the resources occupied by these goroutines. Although we cannot kill a goroutine from the outside, I have to let it end it by itself. We used channel + select to solve this problem before, but some scenarios are more troublesome to implement. For example, the various goroutines derived from a request need to meet certain constraint relationships to achieve some functions such as validity period, aborting the goroutine tree, and passing request global variables.
Save the context
func middleWare(next ) { return (func(w , req *) { ctx := ((),"key","value") (w, (ctx)) }) } func handler(w , req *) { value := ().Value("value").(string) (w, "value: ", value) return } func main() { ("/", middleWare((handler))) (":8080", nil) }
We can save any type of data in the context for delivery and use throughout the life cycle of the request.
Timeout control
func longRunningCalculation(timeCost int)chan string{ result:=make(chan string) go func (){ (*((timeCost))) result<-"Done" }() return result } func jobWithTimeoutHandler(w , r * ){ ctx,cancel := ((), 3*) defer cancel() select{ case <-(): (()) return case result:=<-longRunningCalculation(5): (w,result) } return } func main() { ("/", jobWithTimeoutHandler) (":8080", nil) }
Here, a timerCtx is used to control the execution time of a function. If this time exceeds, it will be forced to interrupt, so that some operations that last longer, such as io, RPC calls, etc.
In addition, another important thing is the usage of cancelCtx instances, which can be used in multiple goroutines, so that the broadcast function of signals can be realized. I will not explain the specific examples here.
Summarize
The context package builds a tree-like Context to achieve the control that the previous layer of Goroutine can pass to the next layer of Goroutine. Some variables can be passed to share, the timeout can be controlled, and the exit of multiple Goroutines can be controlled.
It is said that at Google, Golang programmers are required to pass Context as the first parameter to each function on the ingress request and exit request link. On the one hand, this ensures that the Golang project developed by multiple teams can cooperate well. On the other hand, it is a simple timeout and cancellation mechanism, ensuring the smooth transmission of critical area data in different Golang projects.
Therefore, being good at using context is of great benefit to Golang's development, especially web development.
The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.