go 1.7中引入 父协程想关闭子协程时,调用其context的cancel函数,即会往其通道中发信号。
Context的功能可以从两个方面去体现:发送终止信号去提供给方法;传递一些公用的数据给多个方法。
Context的主要四种结构,CancelContext,TimeoutContext,DeadLineContext,ValueContext的使用
适用场景有哪些?
由于某种原因(超时,或者强制退出)我们希望中止这个goroutine的计算任务,那么就用得到这个Context了
常见的并发控制组件:waitGroup , Context
具体使用场景:
一、rpc使用 , http请求 。归为一类是超时使用 withCancel。 cancel()会被多次调用,context包的cancel调用是幂等的。可以放心多次调用。异步并行控制, 某一个调用失败,不影响别的调用
代码示例
package main
import (
"context"
"fmt"
"math/rand"
"net/http"
"os"
"time"
)
func main(){
//context控制并发 WithCancel cancel()
ctx,cancel := context.WithCancel(context.Background())
go Worker(ctx,"01")
go Worker(ctx,"02")
go Worker(ctx,"03")
time.Sleep(time.Second * 5)
fmt.Println("stop all")
cancel()
time.Sleep(time.Second * 1)
}
func Worker(ctx context.Context,name string ) {
for {
select{
case <- ctx.Done():
fmt.Println(name," stop the goroutine!")
return
default:
fmt.Println(name," ing")
time.Sleep(time.Second *1)
}
}
}
//结果打印:
//01 ing
//02 ing
//02 ing
//03 ing
//01 ing
//01 ing
//02 ing
//03 ing
//02 ing
//01 ing
//03 ing
//01 ing
//02 ing
//03 ing
//stop all
//02 stop the goroutine!
//03 stop the goroutine!
//01 stop the goroutine!
context WithCancel 取消控制 发送取消信号,保证自己这个逻辑中发散出去的goroutine全部成功取消
二、HTTP服务器的request互相传递数据 WithValue。 WithContext 常用于拦截器,cookie验证后,把公用信息存储到request中,接口自行去取
代码示例
// WithValue 使用
func main(){
ContextWithValue()
}
func ContextWithValue() {
rand.Seed(time.Now().Unix())
ctx := context.WithValue(context.Background(), keyID, rand.Int())
operation1(ctx)
}
func operation1(ctx context.Context) {
fmt.Println("operation1 for id:", ctx.Value(keyID), " completed")
operation2(ctx)
}
func operation2(ctx context.Context) {
fmt.Println("operation2 for id:", ctx.Value(keyID), " completed")
}
三、超时请求。 rpc调用 http调用 WithTimeout WithDeadLine(文件io或者网络io等耗时操作可以查看剩余的时间是否充足,,决定是否进行下一步操作)
WithDeadLine 与withTimeout传入的时间参数不一样,其余一样.
WithTimeout代码示例
import (
"context"
"fmt"
"math/rand"
"net/http"
"os"
"time"
)
// WithTimeout
func main(){
// 创建一个空ctx
ctx := context.Background()
// 设置超时时间
cxt, _ := context.WithTimeout(ctx, 1*time.Millisecond)
start := time.Now()
// 创建一个请求 访问谷歌
req, _ := http.NewRequest(http.MethodGet, "http://baidu.com", nil)
// 将带有取消方法的ctx和请求关联
req = req.WithContext(cxt)
client := &http.Client{}
res, err := client.Do(req)
if err!=nil{
fmt.Println("Request failed:", err ," time ",time.Now().Sub(start))
return
}
fmt.Println("Response received, status code:", res.StatusCode)
}
WithTimeout代码示例
func main(){
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Second * 4))
defer cancel()
copyFileWithDeadline(ctx)
time.Sleep(time.Second * 5)
}
func copyFileWithDeadline(ctx context.Context) {
deadline, ok := ctx.Deadline()
if ok == false {
return
}
// deadline.Sub(time.Now()) 截止时间与当前时间的差值
isEnough := deadline.Sub(time.Now()) > time.Second * 5
if isEnough {
fmt.Println("copy file")
} else {
fmt.Println("isEnough is false return")
return
}
}
Context控制并发代码示例,注意协程执行顺序是随机的。但waitgroup是并发控制是可以保证顺序执行
package main
import (
"context"
"fmt"
"math/rand"
"net/http"
"os"
"time"
)
func main(){
//context控制并发 WithCancel cancel()
ctx,cancel := context.WithCancel(context.Background())
go Worker(ctx,"01")
go Worker(ctx,"02")
go Worker(ctx,"03")
time.Sleep(time.Second * 5)
fmt.Println("stop all")
cancel()
time.Sleep(time.Second * 1)
}
func Worker(ctx context.Context,name string ) {
for {
select{
case <- ctx.Done():
fmt.Println(name," stop the goroutine!")
return
default:
fmt.Println(name," ing")
time.Sleep(time.Second *1)
}
}
}
//结果打印:
//01 ing
//02 ing
//02 ing
//03 ing
//01 ing
//01 ing
//02 ing
//03 ing
//02 ing
//01 ing
//03 ing
//01 ing
//02 ing
//03 ing
//stop all
//02 stop the goroutine!
//03 stop the goroutine!
//01 stop the goroutine!
func main(){
// waitgroup来控制并发
wg := sync.WaitGroup{}
wg.Add(2)
go func(){
fmt.Println("1 job")
wg.Done()
}()
go func(){
fmt.Println(" 2 job")
wg.Done()
}()
wg.Wait()
fmt.Println(" end")
}
最后总结:
1. 值传递。 --值传递只是context的一个辅助功能,常见使用:cookie,拦截器或者日志信息、调试信息记录
2. 超时控制 --http设置超时时间,rpc调用,--文件io或者网络io等耗时操作可以查看剩余的时间是否充足,决定是否进行下一步操作
3. 取消控制 --发送取消信号,保证自己这个逻辑中发散出去的goroutine全部成功取消