2017/08/06
 
每次blog.golang.org更新博客,我都迫不及待去读一下;最新的一篇, Contributors Summit,记录了Go贡献者们的一些讨论。我读到一句话,让我感觉得有必要写这个Blog:
 
For instance, it would be nice if io.Reader accepted a context so that blocking read operations could be canceled.(io.Reader接收一个context来取消阻塞的读操作是一个不错的主意)
 
本人瞬间惊呆了,这样一来,io.Reader将会是这样:
type Reader interface { Read(ctx context.Context, p []byte) (n int, err error) }
 
我搜索了一下,发现已经有人向Go2提了这个修改;谢天谢天大多数人并不认可(proposed this change )
 
这篇博客将会讨论所有有关“context”的错误(其实 context还是挺有用的 ),以及Go2需要为它做些什么。
 
Go是一门通用的语言
 
首先我们要明确,Go是一个很好的开发Server程序的语言,但Go并不是专为写Server程序而生。Go是一门通用的语言,就像c,c++,java或python一样。比如我已经使用Go两年了,但却没有写过一个Server程序。
 
因此,我们要以设计一门通用语言的角度来设计Go及其标准库。现在,我想说的是context只适写Server的人,至少大多数情况下如此。
 
Context像病毒一样传染
 
这是context最主要的问题:它会传染!正如这篇博客中所说的(this blog post about the context package
 
At Google, we require that Go programmers pass a Context parameter as the first argument to every function on the call path between incoming and outgoing requests.
(在谷歌,我们要求把context应用到请求从输入到输出所流经的所有函数上面)
 
所有的这些函数都需要传递一个context进去,否则它可能不能被完全Cancel掉。这意味着所有调用的其它库的函数也需要接收context。
 
简而言之,如果你想写一个库,而这个库会被其它的Server程序调用到,那么你就需要在函数中加入context参数!
 
这就是context如何像病毒一样传播。这样有什么坏处呢,我们来看一下:
 
1.Go是一门通用的语言
2.如果一个库会被Server程序调用,它就需要接收context。
3.现在,每个人必须处理context,即使你并不需要它。
 
当然,你可以到处传递context.TODO(),但这简单太糟糕了,它破坏了代码的可读性,让代码看起来丑陋无比,泯灭了我写Go代码的乐趣。
 
如果某天我不得不写出这样的代码:
 
n, err := r.Read(context.TODO(), p)
 
那伙计麻烦给我把枪,让我们Say Goodbye.
 
你也许会辩解:一个库可以为每个函数提供两个版本,一个带context一个不带。额……是的, "database/sql"包就这样做了。尽管它部分解决了这个问题,但看起来很糟糕(不够优雅啊)
 
并且,如果去给学生们教授Go语言,你开始讲解context版的io.Reader接口。学生们问:ctx context.Context是什么东西老师?答案可能会这样:不要管这个,只要传个context.TODO()进来就行了。听起来像 public static void给我的感觉。
 
这里的主要观点是:Context会像病毒一样传播,当我不需要它的话,我不想处理它。
 
“context”包本身的实现并不好。
 
这是一个个人观点。对我来说,context.Context接口有过多的方法。但更主要的问题是:
 
在我的公司里使用ctx.Value,你就会被炒鱿鱼!
 
我不知道是谁想出这个想法让context带上一个无意义的map,(object->object的映射),这有太多问题,我们来列举之:
 
1.很明显的一个,它不是静态类型;
2.它需要记录,哪些Value是哪个函数支持和使用的。我们知道,documentation是永远不会执行的代码。
3.它跟thread-local存储类似。我们知道thread-local存储是多么糟糕的想法。不灵活,使用,测试复杂
4.这不常发生,但是会发生名称冲突。
5.这像一个容易出错的魔法。
 
我知道,ctx.Value会使一些东西实现起来简单。但是我相信,设计API时不使用ctx.Value,你也一定会有替代方案。
 
Context是一个低效的链表。
 
WithCanel,WithDeadline工作需要创建链表。我们需要给WithCancel创建一个goroutine,将cancel信号由前一个context传递到下一个。当然如果context一直没被取消,goroutine就一直存在(相当于resource leak作者认为)
 
最后:
 
ctx context.Context
就像
Foo foo = new Foo();
一样,是Go设计中就极力避免的东西(created to avoid.)。
 
"context"包实际解决了什么问题?
 
即使有这么多的问题,"context"依然是很有用的,因为它解决了Go里面很难处理的一个问题:cancelation(取消)。这是"context"唯一所解决的问题。
 
我们正视现实,Go里面的取消很难实现。在
‘Advanced Go Concurrency Patterns’中有一个很全面和深入的讨论。这个讨论发生在context包引入到Go中之前。因此它讨论的是使用channel来解决cancelation的问题。
 
这个讨论中所提出的解决方案没有可伸缩性,原因如下:
1.cancelation使用的channels不能传递到其它的库或函数,因此只有在内部自己使用。
 
2.想像一个goroutine的树(子gotoutine由父生成),要结束所有的goroutine很容易,只要将cancelation channel关闭即可。但是要结束一个子树却很难。(你需要使用另一个channel,或其它解决方法)
 
"context"解决了这个问题,虽然效率低下且存在很多问题。但这个解决方法却要比已存在的其它办法要好。
 
 
go里我们必须要解决cancelation的问题。当我们使用goroutine时这是必要的。
 
Go2必须正视cancelation的问题!
 
我认为Go提供一个"context"这样的包,本身就是个错误。Go设计的很简单的实现了创建goroutine并在他们之间通信,但是"context"证明Go将goroutine的取消实现的很难用。我认为这个问题需要在语言层面来解决。Go需要在语言层面提供一个解决方案,达到:
 
1.简单,优雅
2.可选 ,非侵入,并非传染
3.健壮,高效
4.只解决cancelation的问题。像Values的功能可以省略了。可以在非常简单的cancelation上实现超时功能。
 
你可能会说:我喜欢context,它不需要改变或使Go语言复杂化,而优雅的解决了这个问题。我不同意。就像所有上面所说的,它不是一个优雅的解决方案,尽管cancellation不是这个语言不可获缺的一部分,但它会变得越来越重要。
 
我想了几种解决方案,但我会在另一篇博客中来写,或者如果有人提出更好的解决方案我会自己保留。这篇博客的目的是指出这个问题。
 
结论:
 
这篇博客试图指出Go语言存在的问题。简而言之,Go语言存在cancelation难的问题,并且"context"包没有很好的解决这个问题。除了语言层面,我没有想出其它解决这个问题的方法。交给Go2来做吧!
 

最新文章

  1. JDBC连接数据库
  2. Spring+SpringMvc+Mybatis框架集成搭建教程五(项目源码发布到GitHub)
  3. mysql与oracle常用函数及数据类型对比
  4. 基于DevExpress开发的GridView如何实现一列显示不同的控件类型
  5. WPF Extended WPF Toolkit
  6. Gradle dsl method not found renderscriptSupportMode()
  7. poj 3417 树形dp+LCA
  8. STL中erase的小心使用
  9. Python学习笔记(四)Python函数的参数
  10. JAVA逆向&反混淆-追查Burpsuite的破解原理(转)
  11. Hbase和Oracle的对比
  12. ADB server didn't ACK failed to start daemon 5037
  13. [Localization] YOLO: Real-Time Object Detection
  14. HDU - 5201 :The Monkey King (组合数 & 容斥)
  15. Activity正确获取View宽高
  16. mysqldump --master-data=2 --single-transaction
  17. HDU1232——畅通工程
  18. 解Bug之路-TCP粘包Bug
  19. hash 冲突及解决办法。
  20. python数据文件读写

热门文章

  1. [LeetCode] 191. Number of 1 Bits ☆(位 1 的个数)
  2. forget suffix word aby able ability out 1
  3. 最佳加法表达式(dp)
  4. Win10系列:VC++绘制几何图形2
  5. xadmin自定义关联菜单
  6. 我在大学毕业后学习Linux系统的心得经验
  7. linux:SSH最简单教程
  8. oo作业总结(二)
  9. flask-security(一)快速入门
  10. tomcat版本对应jee版本等信息