A Tour of Golang (二)
是时候继续总结一波golang使用心得了!码的代码越多了解的go就越多,go处理问题的思路确实不一样 9. defer panic recoverdefer接上次的问题继续讨论,先来看下golang blog上怎么说defer A defer statement pushes a function call onto a list. The list of saved calls is executed after the surrounding function returns. Defer is commonly used to simplify functions that perform various clean-up actions.
个人感觉像是像 C++放在 destructor 中做的事一样,但是仔细考量下来还是不一样 func c() (i int) {
defer func() { i++ }()
return 1
} 函数最终返回的是2而不是1
panicpanic比较好理解,一旦触发了panic,这个内置的函数将会导致整个调用栈退出. recoverrecover用于从panic流程中重新获得控制权.因此recover只能被定义在defer中才会有效果.主动调用recover只会返回nil而且其他什么也不会发生… func main() {
f()
fmt.Println("Returned normally from f.")
}
func f() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f",r)
}
}()
fmt.Println("Calling g.")
g(0)
fmt.Println("Returned normally from g.")
}
func g(i int) {
if i > 3 {
fmt.Println("Panicking!")
panic(fmt.Sprintf("%v",i))
}
defer fmt.Println("Defer in g",i)
fmt.Println("Printing in g",i)
g(i + 1)
}
正常的情况下,输出应该可以预见 Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
Recovered in f 4
Returned normally from f.
如果去掉recover 可以看一下结果 Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
panic: 4
panic PC=0x2a9cd8
[stack trace omitted]
退出的goroutine打印了栈信息然后退出而. 10.接口实现go接口编程十分优雅,采用的是叫做 非嵌入式接口的模型: 传统的面向对象模型中,接口被设计的需要明确的继承关系,这种关系在声明的时候就必须确定,例如 C++ 和 JAVA. 尽管JAVA采用了implement这种方式表述接口,但接口仍然没有脱离C++中的模型:继承与派生之间的强依赖关系. go认为,接口之间的这种依赖应该是单向的.接口的使用者不关心接口的实现方式.接口的实现方也不应该提前知道使用方所有的需求的接口.也就是说接口应该是定义了一种规范,双方都遵循的规范,但是对于双方来讲,并不应该存在依赖关系. go中,一旦有类实现的某个接口所要求的所有的函数,那么就是实现的这个接口.因此go中不应该存在继承树这样的东西:你只需要知道你要提供的是哪些功能. 接口赋值不过,go同样有一些特殊的规定: type Integer int
func (a Integer) less (b Integer) bool {
}
func (a *Integer) bigger (b *Integer) bool {
}
type IntInterface interface{
less (a Integer) bool
bigger (a Integer) bool
}
这种情况下的赋值一个接口对象,应该使用 var a Integer = 1
var b IntInterface = &a
在go中会根据less 自动的生成 对于 *Integer 的less方法,而无法从指针类型的方法推导出非指针的方法. 接口查询 & 类型查询...
if value,ok := object.(type_name); ok{
}
...
也可以更直接的使用switch避免进一步转换 ...
switch v := object.(type){
case int :
case string:
...
11.goroutine 模型&调度goroutine 是go语言并发的实现方式,goroutine其实是一种对于Coroutine的实现。go语言通过简单易用的goroutine使得并发程序非常容易写出来。 看到的知乎上一个系统的回答,拿来引用并且改动了一部分:
一般来说,如果没有显式的让出CPU,就会一直执行当前协程。关于goroutine何时调度,一般会有3种情况:
M:代表真正的内核OS线程,和POSIX里的thread差不多,真正干活的人 G:代表一个goroutine,它有自己的栈,instruction pointer和其他信息(正在等待的channel等等),用于调度。 P:代表调度的上下文,可以把它看做一个局部的调度器,使go代码在一个线程上跑,它是实现从N:1到N:M映射的关键。(这就是为甚么 GOMAXPROCES 可以设置的比系统大的原因) P的数量可以通过GOMAXPROCS()来设置,它其实也就代表了真正的并发度,即有多少个goroutine可以同时运行。 图中灰色的那些goroutine并没有运行,而是出于ready的就绪态,正在等待被调度。P维护着这个队列(称之为runqueue), Go语言里,启动一个goroutine很容易:go func() 就行,所以每有一个go语句被执行,runqueue队列就在其末尾加入一个goroutine,在下一个调度点,就从runqueue中取出一个goroutine执行。 为何要维护多个上下文P?因为当一个OS线程被阻塞时,P可以转而投奔另一个OS线程! 当MO返回时,它必须尝试取得一个context P来运行goroutine 1. 一般情况下,它会从其他的OS线程那里steal偷一个context过来 2. 如果没有偷到的话,它就把goroutine放在一个global runqueue里,然后自己就去睡大觉了(放入线程缓存里)。 Contexts们也会周期性的检查global runqueue,否则global runqueue上的goroutine永远无法执行。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |