1. Basic Principles
1.1 Introduction to Context package
In Go, the Context package is a mechanism for passing request range data, cancel signals, and deadlines. It is often used to handle communication and cancellation between goroutines. The Context package is built in Go and can be used easily without additional dependencies.
The Context package is a lightweight tool that provides a standard interface for passing request-range data, cancel signals, and deadlines between goroutines. The Context package implements a tree-like data structure, where each node represents a request range. Each node has a unique key-value pair, where key is a value of type interface{} and value is a value of any type. The Context package also provides an optional timeout mechanism for automatically canceling requests after a certain period of time.
The core of the Context package is a Context interface that defines methods to obtain request scoped data, cancel requests, and handle timeouts.
type Context interface { Deadline() (deadline , ok bool) Done() <-chan struct{} Err() error Value(key interface{}) interface{} }
- The Deadline() method returns the deadline and a Boolean value indicating whether the deadline has been set.
- The Done() method returns a read-only channel that will be closed when the request is cancelled or timed out.
- The Err() method returns an error indicating why the request was cancelled.
- The Value() method returns the value associated with the given key, and if there is no value, returns nil.
The Context package also provides two functions for creating a Context: WithContext and Background. The Background function returns an empty Context, while the WithContext function creates a new Context based on the given parent Context.
The basic principle of the Context package is to manage request scope data, cancel signals and deadlines by passing Context between goroutines. When a goroutine creates a new goroutine, it passes the Context as a parameter to the new goroutine. The new goroutine can use this Context to access request range data, receive cancel signals, and process timeouts.
1.2 Context creation
In Golang, Context can be created with functions such as WithCancel, WithDeadline, WithTimeout, and WithValue. The usage and precautions of these functions are introduced below.
1.2.1 WithCancel
The WithCancel function can be used to create a Context object and return a cancelable context and a cancel function. When a cancel function is called, all Context objects and their children Context objects are notified, causing them to be canceled.
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
Here is a sample code:
package main import ( "context" "fmt" "time" ) func main() { parent := () ctx, cancel := (parent) go func() { select { case <-(): (()) return case <-(5 * ): ("work done") } }() (10 * ) cancel() (1 * ) }
In the above code, we first create a root Context object parent using the () function, and then create a child Context object ctx using the WithCancel function, and return a cancelable context and a cancel function cancel. Next, we use the select statement in a goroutine to listen for the Done method of the Context object and the return value of the function. If the Done method returns a non-nil error, it means that the Context has been cancelled, otherwise it means that the function has timed out. In the main function, we call the cancel function to notify the Context object and its children Context objects, so that they both cancel execution. Finally, we use the function to make the program wait for a while in order to observe the execution of the Context.
1.2.2 WithDeadline
The WithDeadline function can be used to create a Context object and return a deadline and a cancel function. When the deadline is exceeded, all Context objects and their children Context objects are automatically notified to cancel execution.
func WithDeadline(parent Context, deadline ) (ctx Context, cancel CancelFunc)
Here is a sample code:
package main import ( "context" "fmt" "time" ) func main() { parent := () ctx, cancel := (parent, ().Add(5*)) go func() { select { case <-(): (()) return case <-(10 * ): ("work done") } }() (20 * ) cancel() (1 * ) }
In the above code, we first create a root Context object parent using the () function, and then create a child Context object ctx using the WithDeadline function, and return a deadline and a cancel function cancel. Next, we use the select statement in a goroutine to listen for the Done method of the Context object and the return value of the function. If the Done method returns a non-nil error, it means that the Context has been cancelled, otherwise it means that the function has timed out. In the main function, we call the cancel function to notify the Context object and its children Context objects, so that they both cancel execution. Finally, we use the function to make the program wait for a while in order to observe the execution of the Context.
1.2.3 WithTimeout
The WithTimeout function can be used to create a Context object and return a timeout and a cancel function. When the timeout time is exceeded, all Context objects and their children Context objects are automatically notified to cancel the execution.
func WithTimeout(parent Context, timeout ) (ctx Context, cancel CancelFunc)
Here is a sample code:
package main import ( "context" "fmt" "time" ) func main() { parent := () ctx, cancel := (parent, 5*) go func() { select { case <-(): (()) return case <-(10 * ): ("work done") } }() (20 * ) cancel() (1 * ) }
In the above code, we first create a root Context object parent using the () function, and then create a child Context object ctx using the WithTimeout function, and return a timeout and a cancel function cancel. Next, we use the select statement in a goroutine to listen for the Done method of the Context object and the return value of the function. If the Done method returns a non-nil error, it means that the Context has been cancelled, otherwise it means that the function has timed out. In the main function, we call the cancel function to notify the Context object and its children Context objects, so that they both cancel execution. Finally, we use the function to make the program wait for a while in order to observe the execution of the Context.
1.2.4 WithValue
The WithValue function can be used to create a Context object and return a Context object with the specified value. This value can be any type of data, it can be a basic type, structure, or pointer. It should be noted that this value is only valid in the current Context object and its child Context objects, and is invisible to other Context objects.
func WithValue(parent Context, key interface{}, val interface{}) Context
Here is a sample code:
package main import ( "context" "fmt" ) type userKey struct{} func main() { parent := () ctx := (parent, userKey{}, "admin") go func() { if user, ok := (userKey{}).(string); ok { ("user is %s\n", user) } else { ("user is not found") } }() select {} }
In the above code, we first create a root Context object parent using the () function, and then create a child Context object ctx using the WithValue function, and return a Context object with the specified value. Next, we use the function in a goroutine to get the value in the Context object and determine whether its type is a string type. If yes, output its value, otherwise output "user is not found". In the main function, we use the select statement to keep the program running so that we can observe the execution of the Context.
2. Context usage scenarios
2.1 Concurrent control
A very typical use scenario is that when we need to start multiple goroutines at the same time for task processing, we can use Context to control the execution of these goroutines. In each goroutine, we can detect whether the Context object is cancelled, and if so, the execution of the goroutine is exited, otherwise the execution will continue.
Here is a sample code:
package main import ( "context" "fmt" "sync" ) func worker(ctx , wg *) { defer () for { select { default: ("work") case <-(): return } } } func main() { parent := () ctx, cancel := (parent) var wg for i := 0; i < 3; i++ { (1) go worker(ctx, &wg) } cancel() () }
In the above code, we first create a root Context object parent using the () function, and then create a child Context object ctx using the WithCancel function, and return a cancel function cancel. Next, we use , to wait for all goroutine execution to complete. In the main function, we start three goroutines to execute the task, and use the cancel function to notify these goroutines to cancel execution. Finally, we use the Wait method to wait for all goroutine execution to complete.
2.2 Timeout control
Another typical use scenario is that when we need to set a timeout time for an operation, we can use Context to control the execution time of the operation. When the operation execution timeout, we can notify the Context object and its children Context object to cancel execution.
Here is a sample code:
package main import ( "context" "fmt" "time" ) func work(ctx ) { for { select { default: ("work") case <-(): ("work done") return } } } func main() { parent := () ctx, cancel := (parent, *5) defer cancel() work(ctx) }
In the above code, we first create a root Context object parent using the () function, and then create a child Context object ctx using the WithTimeout function, and return a cancel function cancel. In the work function, we start an infinite loop and continuously output "work". At the same time, we use the select statement to wait for the Context object to be cancelled. In the main function, we call the cancel function using the defer statement to ensure that the Context object is cancelled. Since we set a 5-second timeout in the WithTimeout function, the work function stops executing when the program runs for more than 5 seconds.
2.3 Database connection
When using database connections, we usually need to ensure that the number of connections in the connection pool does not exceed a certain threshold. If the number of connections in the connection pool exceeds the threshold, you need to wait for the connection to be released before operating. In this case, we can use Context to control the life cycle of the connection.
Here is a sample code:
package main import ( "context" "database/sql" "fmt" "sync" "time" _ "/go-sql-driver/mysql" ) const maxConn = 5 func main() { db, err := ("mysql", "root:root@tcp(127.0.0.1:3306)/test") if err != nil { panic(err) } defer () ctx, cancel := ((), *5) defer cancel() connCh := make(chan *, maxConn) var wg for i := 0; i < maxConn; i++ { (1) go func() { defer () for { select { case <-(): return default: if len(connCh) < maxConn { conn, err := (ctx) if err != nil { (err) return } connCh <- conn } } } }() } () }
In the above code, we first use the function to open a connection to a MySQL database and return a DB object db. Next, we use the WithTimeout function to create a Context object ctx and set a timeout of 5 seconds. At the same time, we create a channel object connCh with a capacity of maxConn to store database connections. In g oroutine, we use the select statement to wait for the Context object to be cancelled. In each loop, we check if the number of connections in the connection pool exceeds the threshold, and if not, use the function to get a new connection from the connection pool and store it in connCh. Finally, we use Wait for all goroutine execution to complete.
2.4 HTTP Request
When using HTTP requests, we usually need to set a timeout time to ensure that the request can be responded within the specified time. In this case, we can use Context to control the execution time of HTTP requests.
Here is a sample code:
package main import ( "context" "fmt" "io/ioutil" "net/http" "time" ) func main() { client := ctx, cancel := ((), *5) defer cancel() req, err := (ctx, , "", nil) if err != nil { (err) return } resp, err := (req) if err != nil { (err) return } defer () body, err := () if err != nil { (err) return } (string(body)) }
In the above code, we first create an HTTP client object client using . Next, we use the WithTimeout function to create a Context object ctx and set a timeout of 5 seconds. At the same time, we use the function to create an HTTP request object req and pass the Context object ctx as a parameter to the function. In the Do function, we will automatically pass the Context object ctx to the HTTP request and automatically cancel the request after the timeout time arrives.
2.5 gRPC request
When using gRPC requests, we usually need to set a timeout to ensure that the request can be responded within the specified time. In this case, we can use Context to control the execution time of gRPC requests.
Here is a sample code:
package main import ( "context" "fmt" "log" "time" pb "/example/helloworld" "/grpc" ) const ( address = "localhost:50051" defaultName = "world" ) func main() { conn, err := (address, ()) if err != nil { ("did not connect: %v", err) } defer () c := (conn) ctx, cancel := ((), *5) defer cancel() r, err := (ctx, &{Name: defaultName}) if err != nil { ("could not greet: %v", err) } ("Greeting: %s", ()) }
In the above code, we first use the function to create a gRPC client connection object conn. Next, we use the function to create a GreeterClient object c. Then, we use the WithTimeout function to create a Context object ctx and set a timeout of 5 seconds. Finally, we use the SayHello function of the GreeterClient object c to send a gRPC request and pass the Context object ctx as a parameter to the function. In the SayHello function, we will automatically pass the Context object ctx to the gRPC request and automatically cancel the request after the timeout time arrives.
3. Summary
Through this article, we understand the basic principles, usage methods and sample code of Golang's Context package. The Context package is one of the important tools in the Go language to implement concurrent control and timeout control, which can help us control program execution more flexibly. In practical applications, we can use the Context package to implement some complex functions, such as controlling database connection pooling, handling HTTP requests and gRPC requests. By learning and using the Context package, we can better implement concurrent control and timeout control, improving the reliability and stability of the program.
When using the Context package, you need to pay attention to the following points:
- The Context object is thread-safe and can be accessed by multiple goroutines at the same time.
- You can use the WithCancel, WithDeadline, and WithTimeout functions to create a Context object and use the function to create a root Context object.
- When communicating between goroutines, you can use the Context object to deliver messages and control the execution of goroutines.
- When using a third-party library, you need to pay attention to whether the library supports the Context function in order to use the Context package correctly.
- When using the Context package, you need to avoid Context abuse. You should only pass the necessary Context objects according to actual needs to avoid passing the Context objects to too many functions, making it difficult for the program to maintain and debug.
Through the introduction of this article, I believe you have a deeper understanding and mastery of Golang's Context package. I hope you can flexibly use the Context package in practical applications to improve the efficiency and reliability of your program.
The above is an article that will help you understand the detailed content of the Golang Context package in depth. For more information about the Golang Context package, please follow my other related articles!