golang是一门简明,强劲,安全的编程语言。我喜欢他支持指针同时提供了各种安全机制减低烧脑度。比如:
func getIntegerAddress() *int{
a:=10
return &a
}
这玩意放在c++里边你会错到欲仙欲死,可是用golang这就是一个正常实现(虽然有点二)。而且go的performance不差。
如果你跟我一样是个非常弱逼的c/c++程序员,看到这里的你应该已经比较激动了。
别急,还有些其他牛逼闪闪的东西呢。
channel和goroutine。
你制造了个东西,你知道有人会处理它,可是你不知道具体是谁。可能你制造这玩意特别快,但是处理的人比较慢。一个不太形象的比喻是,你可以呸呸呸呸瞬间吐出十口痰,然后清洁卫生的大妈要一把诅咒你全家还有你未出生的孩子的菊花同时用各种工具去清理这些玩意。我们可以想象,一位大妈是没法很快搞定你的十口浓痰的,我们可能需要n位大妈一起合作。如果是别的什么语言,咱们可能就要生成一个大妈的worker pool,并且像个办法把代表这些浓痰的memeory共享出来给大妈们。
如果用golang这个骚骚的工具,我们可以写出这样的代码来:
package main
import "time"
var phlegms = make(chan int) //这里存所有吐得痰
func spit(phlegm int) { //吐痰函数
phlegms <- phlegm
}
func cleanItUp() {//大妈函数
for {
select {
case phlegm := <-phlegms:
println("I cleaned ", phlegm)
}
}
}
func main() {
for i := 0; i < 1000; i++ {
go spit(i)//连吐1000口痰
}
for i := 0; i < 10; i++ {
go cleanItUp()//生成10个大妈
}
<-time.After(5 * time.Second)
}
跟我一样的concurrency苦手们,看到这里是不是已经按捺不住要跟golang来一发了?
是时候用几个golang的小坑适当的平衡一下你们的热情了,否则可能每个人都要雇佣几位大妈帮你们cleanup一下你们的屏幕。
func httpHandler() {
resultCh := make(chan bool)
go func() {
defer close(resultCh)
resultCh <- someTimeConsumingRPC()
}()
select {
case result := <-resultCh:
println(result)
case <-time.After(1 * time.Second):
println("shit")
}
}
我们假设someTimeConsumingRPC() 是一个不太稳定的调用,有时候它超过一秒钟才返回。那么恭喜你,你的http service将会:内存泄漏!
原因是:goroutine不能强制杀掉!当超过了一秒钟,没人读resultCh的情况下,写result的goroutine永远无法完成!每个goroutine占用8k以上内存,当你这样卡死的goroutine多了,你的内存占用量和goroutine总数就会居高不下,并且最终导致你的服务器内存不足,服务崩溃。
可能有的老湿想跳出来说:楼主sb。给resultCh一个buffer不就完事了么?是的,这样做这个例子就行得通了,但是buffered channel是不是万能的呢?我们再看一个例子
package main
import "fmt"
func httpHandler() {
errCh := make(chan error, 1)
resultCh := make(chan int, 1)
go func() {
defer close(errCh)
defer close(resultCh)
errCh <- fmt.Errorf("shit")
}()
select {
case <-errCh:
case <-resultCh:
println("this shall not happen dude!")
}
}
func main() {
for i := 0; i < 1000000; i++ {
httpHandler()
}
}
这个例子特别有意思。运行100万次,我自己的mbp上面出现了不到10次"this shall not happen dude!"的log。
聪明的你,这是为什么呢?