简析go语言并发模式(4)
超时和取消模式:超时模式在超时返回后,已经创建的goroutine可能依然处于等待状态,没有返回,也没有被回收,依然占用资源。这种情况下一般使用Go的context包来实现取消模式。Go在1.17标准库中引入context包。
import (
"context"
"errors"
"io"
"log"
"net/http"
"net/http/httptest"
"time"
)
type result struct {
value string
}
func requestData(servers ...*httptest.Server) (result, error) {
c := make(chan result, len(servers))
// http包支持利用context.Context的超时和取消机制
// 利用context.WithCancel创建一个可以取消的context.Context变量ctx
ctx, cancel := context.WithCancel(context.Background())
// 函数返回前取消request请求
defer cancel()
queryFunc := func(i int, server *httptest.Server) {
url := server.URL
req, err := http.NewRequest("GET", url, nil)
if err != nil {
log.Printf("query goroutine-%d: http NewRequest error: %s", i, err)
return
}
// 使用context.Context变量ctx
req = req.WithContext(ctx)
log.Printf("query goroutine-%d: send request...\n", i)
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Printf("query goroutine-%d: get return error: %s\n", i, err)
return
}
log.Printf("query goroutine-%d: get response\n", i)
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
c <- result{
value: string(body),
}
return
}
for i, serv := range servers {
go queryFunc(i, serv)
}
select {
case r := <-c:
return r, nil
case <-time.After(500 * time.Microsecond):
return result{}, errors.New("timeout")
}
}
func fakeServer(name string, interval int) *httptest.Server { // 使用httptest包NewServer函数创建虚拟server
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Printf("%s receive a http request\n", name)
time.Sleep(time.Duration(interval) * time.Microsecond)
w.Write([]byte(name + " :ok"))
}))
}
func main() {
res, err := requestData(
fakeServer("server-1", 200),
fakeServer("server-2", 1000),
fakeServer("server-3", 600),
)
if err != nil {
log.Printf("invoke requestData error: %s", err)
return
}
log.Println(res)
time.Sleep(10 * time.Second)
}
$ go run go-concurrency-pattern.go
2023/05/08 23:28:36 query goroutine-2: send request...
2023/05/08 23:28:36 query goroutine-1: send request...
2023/05/08 23:28:36 query goroutine-0: send request...
2023/05/08 23:28:36 invoke requestData error: timeout
Go语言基础及实战 文章被收录于专栏
Go语言学习笔记、语法知识、技术要点和个人理解及实战