Go并发模式之context(比done channel 更加强大)-取消goroutine的好的解决方案
时间:2019-04-19
本文章向大家介绍Go并发模式之context(比done channel 更加强大)-取消goroutine的好的解决方案,主要包括Go并发模式之context(比done channel 更加强大)-取消goroutine的好的解决方案使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
在并发程序中, 由于超时、取消、或系统的其他部分的故障 往往需要抢占操作。 done channel 可以在你的程序中流动并取消所有阻塞的并发操作。 看起来不错,但是还不完美,还不是很够用。 什么样的功能还需要呢?什么样功能才算完美呢? 如果我们可以在简单的通知上附加传递额外的信息; 如 为什么取消发生, 函数是否有需要完成的最后期限(超时), 这些情况下这些功能 非常有用。
context 包有如下方法: todo context(type 类型): 有: 一个Deadline 函数,用于指示在一定时间之后goroutine 是否被取消, 一个 Err方法, 如果goroutine 被取消,将返回非零。 一个Value 方法,
函数中取消有三种情况: 1。 goroutine 的 父 goroutine 可能想要取消它。 2。 一个goroutine 可能想要取消它的 子goroutine 3。 goroutine 中呢任何阻塞操作都必须是可抢占的, 以便它可以被取消。
下面看看 之前 使用 do channel 方式 有什么优缺点
func printGreeting(done <-chan interface{}) error{
greeting, err := genGreeting(done)
if err != nil{
return err
}
fmt.Printf("%s world!\n", greeting)
return nil
}
func genGreeting(done <-chan interface{})(string, error){
switch locale, err := locale(done);{
case err != nil:
return "", err
case locale == "EN/US":
return "hello", nil
}
return "", fmt.Errorf("unsupported locale")
}
func locale(done <-chan interface{})(string, error){
select{
case <-done :
return "", fmt.Errorf("canceled")
case <-time.After(1*time.Minute):
}
return "EN/US", nil
}
func printFarewell(done <-chan interface{}) error{
farewell, err := genFarewell(done)
if err != nil{
return err
}
fmt.Printf("%s world!\n", farewell)
return nil
}
func genFarewell(done <-chan interface{})(string, error){
switch locale,err := locale(done);{
case err != nil:
return "", err
case locale == "EN/US":
return "goodbye", nil
}
return "", fmt.Errorf("unsupproted locale")
}
func main() {
var wg sync.WaitGroup
done := make(chan interface{})
defer close(done)
wg.Add(1)
go func() {
defer wg.Done()
if err := printGreeting(done); err != nil{
fmt.Println("%v", err)
return
}
}()
wg.Add(1)
go func() {
defer wg.Done()
if err := printFarewell(done); err != nil{
fmt.Printf("%v", err)
}
}()
wg.Wait()
}
假设有这样需求: 假设genGreeting 只想在放弃调用locale之前等待 1s(超过1s 就不想调用了),超时时间 1s; 如果 printGreeting 不成功,我们也想取消 对 printFarewall的调用。 毕竟不打招呼,说再见就没有意义了。使用do channel 难以做到这点,下面 看看context 能不能满足这样的需求?
func main() {
var wg sync.WaitGroup
//Background() 方法 返回一个空的上下文
// withCancel 包装它 为了 获取 cancel/取消 方法/功能
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
wg.Add(1)
go func() {
defer wg.Done();
if err := printGreeting(ctx); err != nil{
fmt.Printf("cannot print greeting: %v\n", err)
//在这一行,如果打印问候出错,将 取消上下文; 或说对上下文 环境 执行 cancel, 这样使用这个上下文或者从这个上下文衍生出来的上下文。
//中 ct.Done() 将收到信号/收到值; 进而达到解除阻塞 的目的。
cancel()
}
}()
wg.Add(1)
go func() {
defer wg.Done()
if err := printFarewell(ctx); err != nil{
fmt.Printf("cannot print farewell: %v\n", err)
}
}()
wg.Wait()
}
func printGreeting(ctx context.Context) error{
greeting, err := genGreeting(ctx)
if err != nil{
return err
}
fmt.Printf("%s world!\n", greeting)
return nil
}
func genGreeting(ctx context.Context)(string, error){
ctx, cancel := context.WithTimeout(ctx, 1*time.Second)
defer cancel()
switch locale, err := locale(ctx);{
case err != nil :
return "", err
case locale == "EN/US":
return "hello", nil
}
return "", fmt.Errorf("unsupported locale")
}
func locale(ctx context.Context)(string, error){
select{
case <-ctx.Done():
return "", ctx.Err()
case <-time.After(1* time.Minute) :
}
return "EN/US", nil
}
func printFarewell(ctx context.Context) error{
farewell, err := genFarewell(ctx)
if err != nil{
return err
}
fmt.Printf("%s world!\n", farewell)
return nil
}
func genFarewell(ctx context.Context)(string, error){
switch locale, err := locale(ctx);{
case err != nil:
return "", err
case locale == "EN/US":
return "goodbye", nil
}
return "", fmt.Errorf("unsupported locale")
}
//cannot print greeting: context deadline exceeded
//cannot print farewell: context canceled
总结: cancel() 或者 时间到了(context.WithTimeout(ctx, 1*time.Second)) 都会 使 ctx.Done() 收到值, 解除阻塞
那么 Deadline 又是在什么情况下。或有那些需求时会用到?
func locale(ctx context.Context)(string, error){
if deadline, ok := ctx.Deadline(); ok{
if deadline.Sub(time.Now().Add(1*time.Minute)) <= 0 {
fmt.Println("end locale")
return "", context.DeadlineExceeded
}
}
select{
case <- ctx.Done():
return "", ctx.Err()
case <-time.After(1*time.Minute):
}
return "EN/US", nil
}
//end locale //cannot print greeting: context deadline exceeded //cannot print farewell: context canceled /** 在这里我们检查我们的上下文是否提供了截止。 如果提供了,并且 能够确定 当前时间 再执行下去一定会超时, 那么程序就立刻返回,不往下走业务逻辑; 可以返回上下文包中的特定错误, 即 DeadlineExceeded 在调用下一个 成本很高的函数的程序中, 这可能会节省大量的时间,它允许 该函数立即失败,而不必等待实际的超时发生。 前提/适用的情况是 必须知道 下级调用图 需要多长时间,这个可能实践起来非常困难。
图:todo
实际上 超时时间设置 肯定大于 正常情况下 程序的执行时间;如 假如 locale 一般 要执行 1min ;那么超时时间 就是 1min + ;(本例子中 是为了看到 效果 才将 超时时间设置为1s) 决定 deadline 是否满足 <=0 的关键 在于 now位置(它是变化的);实际上 意思是: 如果前面执行的时间太长了(出现意外情况);那么后面也就没必要 再执行了,因为执行了肯定会超时的!
如何在上下文中存储数据以及如何检索数据:
func main(){
ProcessRequest("jane", "abc123")
}
func ProcessRequest(userID, authToken string){
ctx := context.WithValue(context.Background(), "userID", userID)
ctx = context.WithValue(ctx, "authToken", authToken)
HandleResponse(ctx)
}
func HandleResponse(ctx context.Context){
fmt.Printf("handling response for %v (%v)",
ctx.Value("userID"), ctx.Value("authToken"))
}
存储 使用 context.WithValue(....); 检索使用 ctx.Value() 正确使用这个功能 ,需要注意的是:/要满足的条件是: 1。 使用的键 必须满足 可比较特性/特点; 也就是 使用运算符== 和 != 在使用时要返回正确的结果 2。 返回值必须安全, 才能从多个goroutine 访问。 具体怎么做呢?
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- codeforces 349B(贪心)
- codeforces 1311D(暴力)
- codeforces 1382C1(思维)
- java学习原理篇|如何学习使用一个新工具
- codeforces 545C(贪心)
- codeforces 1272D(dp)
- codeforces 1203D2(贪心)
- HDOJ 2112(最短路)
- codeforces 1384A(构造)
- codeforces 982C (dfs)
- POJ 2493 (map)
- codeforces 1417C(思维)
- codeforces 1256C (贪心+构造)
- codeforces 722C(带权并查集+反向思维)
- codeforces 1144D(思维)