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来做吧!
 

【翻译】Context should go away for Go 2的更多相关文章

  1. Tomcat翻译--Context Container

    原文:http://tomcat.apache.org/tomcat-7.0-doc/config/context.html#Resource_Definitions The Context Cont ...

  2. 第九篇:在SOUI中使用多语言翻译

    为UI在不同地区显示不同的语言是产品国际化的一个重要要求. 在SOUI中实现了一套类似QT的多语言翻译机制:布局XML不需要调整,程序代码也不需要调整,只需要为不同地区的用户提供不同的语言翻译文件即可 ...

  3. 绝对让你理解Android中的Context

    这个问题是StackOverFlow上面一个热门的问题What is Context in Android? 整理这篇文章的目的是Context确实是一个非常抽象的东西.我们在项目中随手都会用到它,但 ...

  4. 用XPath精确定位节点元素&selenium使用Xpath定位之完整篇

    在利用XSL进行转换的过程中,匹配的概念非常重要.在模板声明语句 xsl:template match = ""和模板应用语句xsl:apply-templates select ...

  5. 【翻译】Android避免内存泄露(Activity的context 与Context.getApplicationContext)

    原谅地址:http://android-developers.blogspot.com/2009/01/avoiding-memory-leaks.html ,英文原文在翻译之后 Android 应用 ...

  6. [翻译].NET Shell Extensions - Shell Context Menus---.net 外壳扩展-右键菜单

    我自己的前言说明: 本文原作者为    Dave Kerr,原文链接为.NET Shell Extensions - Shell Context Menus:,我是在为了完成最新需求的时候查询资料的时 ...

  7. 翻译 | Placing Search in Context The Concept Revisited

    翻译 | Placing Search in Context The Concept Revisited 原文 摘要 [1] Keyword-based search engines are in w ...

  8. 《Django By Example》第四章 中文 翻译 (个人学习,渣翻)

    书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:祝大家新年快乐,这次带来<D ...

  9. [翻译]开发文档:android Bitmap的高效使用

    内容概述 本文内容来自开发文档"Traning > Displaying Bitmaps Efficiently",包括大尺寸Bitmap的高效加载,图片的异步加载和数据缓存 ...

随机推荐

  1. 如何在Linux中tomcat下运行一个web项目

    如何在Linux中tomcat下运行一个web项目 然后启动Tomcat项目.运行的运行后会自动将war包解压. 如果页面报404,那么请查看tomcat日志文件,它一定是报错了....

  2. error TS2304: Cannot find name 'Promise' && TS2307: Cannot find module '**'

    error TS2304: Cannot find name 'Promise' 解决方法:在编译选项中加入"target":"es6" { "ver ...

  3. hdu3377

    题解: 简单的插头dp 加上一个代价即可 代码: #include<cstdio> #include<cmath> #include<cstring> #inclu ...

  4. django搭建博客

    https://andrew-liu.gitbooks.io/django-blog/content/index.html

  5. Integer与int的区别(转)

    如果面试官问Integer与int的区别:估计大多数人只会说道两点,Ingeter是int的包装类,int的初值为0,Ingeter的初值为null.但是如果面试官再问一下Integer i = 1; ...

  6. Invalid MEX-file: caffe.mexa64 的解决方案

    http://blog.csdn.net/iamzhangzhuping/article/details/53105708

  7. Asp.Net WebApi核心对象解析(一)

    生活需要自己慢慢去体验和思考,对于知识也是如此.匆匆忙忙的生活,让人不知道自己一天到晚都在干些什么,似乎每天都在忙,但又好似不知道自己到底在忙些什么.不过也无所谓,只要我们知道最后想要什么就行.不管怎 ...

  8. SQL-19 查找所有员工的last_name和first_name以及对应的dept_name,也包括暂时没有分配部门的员工

    题目描述 查找所有员工的last_name和first_name以及对应的dept_name,也包括暂时没有分配部门的员工CREATE TABLE `departments` (`dept_no` c ...

  9. centos7中docker操作

    docker部署nginx 1. 下载nginx [root@localhost my.Shells]# docker images REPOSITORY TAG IMAGE ID CREATED S ...

  10. fastjson 在 springboot中的运用

    题记: 项目中开始用是Gson,但是压力测试的时候会出现性能下降明显,不得已换成了fastjson 1.首先引用包 <dependency> <groupId>com.alib ...