为什么需要context
在go服务器中,对于每个请求的request都是在单独的goroutine中进行的,处理一个request也可能设计多个goroutine之间的交互, 使用context可以使开发者方便的在这些goroutine里传递request相关的数据、取消goroutine的signal或截止日期
在并发程序中,由于超时、取消 *** 作或者一些异常情况,往往需要进行抢占 *** 作或者中断后续 *** 作。熟悉channel的朋友应该都见过使用done channel来处理此类问题。比如以下这个例子:
上述例子中定义了一个buffer为0的channel done, 子协程运行着定时任务。如果主协程需要在某个时刻发送消息通知子协程中断任务退出,那么就可以让子协程监听这个done channel,一旦主协程关闭done channel,那么子协程就可以推出了,这样就实现了主协程通知子协程的需求。这很好,但是这也是有限的。
如果我们可以在简单的通知上附加传递额外的信息来控制取消:为什么取消,或者有一个它必须要完成的最终期限,更或者有多个取消选项,我们需要根据额外的信息来判断选择执行哪个取消选项。
考虑下面这种情况:假如主协程中有多个任务1, 2, …m,主协程对这些任务有超时控制;而其中任务1又有多个子任务1, 2, …n,任务1对这些子任务也有自己的超时控制,那么这些子任务既要感知主协程的取消信号,也需要感知任务1的取消信号。
如果还是使用done channel的用法,我们需要定义两个done channel,子任务们需要同时监听这两个done channel。嗯,这样其实好像也还行哈。但是如果层级更深,如果这些子任务还有子任务,那么使用done channel的方式将会变得非常繁琐且混乱。
我们需要一种优雅的方案来实现这样一种机制:
上层任务取消后,所有的下层任务都会被取消;中间某一层的任务取消后,只会将当前任务的下层任务取消,而不会影响上层的任务以及同级任务。
这个时候context就派上用场了。我们首先看看context的结构设计和实现原理。
context接口
先看Context接口结构,看起来非常简单。
}
Context接口包含四个方法:
Deadline返回绑定当前context的任务被取消的截止时间;如果没有设定期限,将返回ok == false。
Done 当绑定当前context的任务被取消时,将返回一个关闭的channel;如果当前context不会被取消,将返回nil。
Err 如果Done返回的channel没有关闭,将返回nil;如果Done返回的channel已经关闭,将返回非空的值表示任务结束的原因。如果是context被取消,Err将返回Canceled;如果是context超时,Err将返回DeadlineExceeded。
Value 返回context存储的键值对中当前key对应的值,如果没有对应的key,则返回nil。
可以看到Done方法返回的channel正是用来传递结束信号以抢占并中断当前任务;Deadline方法指示一段时间后当前goroutine是否会被取消;以及一个Err方法,来解释goroutine被取消的原因;而Value则用于获取特定于当前任务树的额外信息。而context所包含的额外信息键值对是如何存储的呢?其实可以想象一颗树,树的每个节点可能携带一组键值对,如果当前节点上无法找到key所对应的值,就会向上去父节点里找,直到根节点。
emptyCtx
emptyCtx是一个int类型的变量,但实现了context的接口。emptyCtx没有超时时间,不能取消,也不能存储任何额外信息,所以emptyCtx用来作为context树的根节点。
Background和TODO只是用于不同场景下: Background通常被用于主函数、初始化以及测试中,作为一个顶层的context,也就是说一般我们创建的context都是基于Background;而TODO是在不确定使用什么context的时候才会使用。
用法 :
因为内存管理粗糙。经常看到fmtxxx导致内存占用太多,反射导致内存占用太多的抱怨。
go语言适合写服务器组件,那种和业务数据无关的服务器。比如数据库服务器、web服务器、日志搜索引擎等。如果用来写一个crm管理系统,非常累,因为缺乏好多高级特性和庞大的第三方库,而且语法比较单一,总体感觉就跟写命令行差不多。
Go语言是谷歌2009发布的编程语言,这个语言发明的目的,就是为了在运行速度接近C/C++语言的基础上(注意是接近),降低开发者的门槛,减少开发难度。
Go语言,在功能上没有超过C/C++,适用者为没有C/C++经验的开发者,开发出接近C效率的程序。对于已经熟练掌握C/C++的开发者来说,Go语言没有优势,还要重学语法,适应开发环境,明显是不符合效率的。
总结
其实语言这东西,都有其优势和劣势。而且有些东西并不是纯技术的。比如java的优势在于清晰的语意表达。写代码的上限不高,但是下限也不低,适合工业开发。
而go呢,不得不说go在微服务这块有先天优势。毕竟java中要实现go的很多功能,需要引入第三方库。很笨重。而go原生支持,这个微服务就很轻巧。但是go的语法太活,工业用是一个挺大的弊端。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)