2026-05-27
go
0

目录

题 1
题 2
题 3
题 4
shell
context是一个树形结构,最顶层就是context.Background(),这个context是初始的context,不能取消,用于派生后面的子context 而context.TODO()获得一个空context,类似context.Background(),但是这个在代码中代表一个暂不确定用法的占位 而生成context之前有下面的方式: 1. context.Background()或者context.TODO() : 获得根context,一般用于创建首个context的时候用 2. func WithValue(parent Context, key, val any) Context :创建带参键值对的context 3. func WithCancel(parent Context) (ctx Context, cancel CancelFunc) : 创建带cancel函数的,必须手动执行cancel 4. func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) : 创建带截至时间的,一般用于链式传播 5. func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) : 创建超时contxt,用于单任务,而如果链式传播使用这个,容易有误差,比如同时创建两个协程,都使用timeout,那么先创建的比后创建的总是快一点,使用deadline就不会有这种问题 6. func WithCancelCause(parent Context) (ctx Context, cancel CancelCauseFunc) 7. func WithTimeoutCause(parent Context, timeout time.Duration, cause error) (Context, CancelFunc) 6和7都是可以传递自定义的错误,比如超时context,如果超时了,通过下面例子展示:
go
package main import ( "context" "fmt" ) func main() { ctx, cancel := context.WithCancelCause(context.Background()) err := fmt.Errorf("test error: %s", "my fault") cancel(err) fmt.Println(ctx.Err()) // 返回取消错误 fmt.Println(context.Cause(ctx)) // 返回自定义错误 } PS D:\goProject\duping> go run .\main.go context canceled test error: my fault

题 1

go
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) cancel() // 手动取消 time.Sleep(6 * time.Second) fmt.Println(ctx.Err()) // 输出什么?为什么? 输出:context canceled而不是context.deadlineExceeded 因为时间到了,上级context主动取消,时间到了导致的取消是context.deadlineExceeded

题 2

go
ctx := context.WithValue( context.WithValue(context.Background(), "a", 1), "a", 2, ) fmt.Println(ctx.Value("a")) // 输出什么? 输出的是2,因为子context的参数覆盖了父context的参数

题 3

下面代码有什么问题?

go
func handler(w http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second) result, err := doWork(ctx) // 忘了 cancel() if err != nil { http.Error(w, err.Error(), 500) return } fmt.Fprint(w, result) } 没有defer cancel(),导致可能有遗留的协程占用资源

题 4

写一个函数:传入一组 URL,并发请求,2 秒超时,任意一个失败则取消全部,返回所有成功结果。

go
package main import ( "context" "fmt" "math/rand" "sync" "time" ) var testURLs = []string{ "http://example.com", "https://www.example.com", "https://httpbin.org/get", "https://httpbin.org/get?name=test&id=1", "https://jsonplaceholder.typicode.com/posts/1", "https://via.placeholder.com/300x200.png", "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf", "http://httpbin.org/redirect-to?url=https://example.com", "https://httpbin.org/status/404", "https://httpbin.org/delay/2", "wss://echo.websocket.org", "https://httpbin.org/bytes/1048576", "http://localhost:8080/api/health", } func DealUrl(url string) *response { msg := &response{ url: url, ok: rand.Intn(10000)%145 != 0, } return msg } type response struct { url string ok bool } func main() { ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(2*time.Second)) defer cancel() var wg sync.WaitGroup result := make(chan *response) worker := func(c context.Context, r chan<- *response) { defer wg.Done() url := c.Value("url") urlstring := fmt.Sprintf("%v", url) for { msg := DealUrl(urlstring) select { case <-c.Done(): return case r <- msg: continue } } } for _, url := range testURLs { wg.Add(1) c := context.WithValue(ctx, "url", url) go worker(c, result) } for { msg, cok := <-result fmt.Println(msg.url) if !msg.ok && cok { fmt.Println("not OK=======>", msg.url) cancel() break } } wg.Wait() }