前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Golang 语言特性总结

Golang 语言特性总结

作者头像
Check King
发布2021-08-09 10:47:01
3540
发布2021-08-09 10:47:01
举报

简介

这几年主要从事golang的后台开发,这里总结一下golang的一些特性,这篇文章不会面面俱到,只是把我认为重要的点记录下来。

goroutine实现机制

TODO

golang垃圾回收

TODO

channel

通道本身是并发安全的,在声明并初始化通道的时候,需要用make。通道包含容量,容量为0的称作非缓冲通道,容量大于0的,称作缓冲通道。

非缓冲通道需要发送和接收成对的时候才正式执行

代码语言:javascript
复制
ch := make(chan int)
go func() {
   ch <- 1
}()
go func() {
   x :<- ch
}

也就是,对于发送chan协程来说,接收协程没有ready, 它是不执行的,对于接收chan协程来说,发送协程没有ready, 它也是不执行的。

带缓冲的通道发送端只要缓冲区没满,就可以发送数据而不阻塞,接收方只要缓冲区有数据,就能接收数据不阻塞。

代码语言:javascript
复制
ch := make(chan int,3)
go func() {
   ch <- 1
   ch <- 2
   ch <- 3
}()
go func() {
   x :<- ch
   y :<- ch
   z :<- ch
}

通道的一些异常操作情况总结:

nil

empty

full

not full

closed

接收

阻塞

阻塞

不阻塞

不阻塞

返回未读的元素,读完后返回零值

发送

阻塞

成功

阻塞

成功

panic

关闭

panic

关闭成功,无未读元素

关闭成功,有未读元素

关闭成功,有未读元素

panic

select 和channel的结合使用:

select中如果只有case 语句,如果所有case对应的channel均无值返回则select阻塞。如果有default语句,则没有事件的时候,会执行default语句。

如果select中包含多条case语句有值返回,select为了公平性,会随机选择一个case语句执行,而不是按case语句的顺序执行。

panic, recover and defer

golang的程序如果出现一些异常,比如数组越界,nil pointer访问等,就会发生panic, panic会导致程序崩溃,即使panic发生在子协程中,也会导致整个进程崩溃。

因此,我们在实际项目开发的时候,都会用recover的方式截获panic, 避免程序崩溃,这就有点类似java中的try...catch了。

代码语言:javascript
复制
func (s Service) GetCommentList(ctx context.Context, req *proto.GetCommentListReq) (resp *proto.GetCommentListResp,
	err error) {
	defer func() {
		if e := recover(); e != nil {
			log.Errorf(ctx, "got_a_panic|err=%v", common.GetStackInfo())
			err = access.NewError(ctx, common.ErrorInternalError, "server recover panic")
		}
	}()
	resp, err = comment.GetCommentList(ctx, req)
	if err != nil {
		err = access.NewError(ctx, common.ErrorGetCommentList, err.Error())
	}
	return resp, err
}

// 其中common.GetStackInfo()的实现
func GetStackInfo() string {
	var buf [4096]byte
	n := runtime.Stack(buf[:], false)
	return string(buf[:n])
}

我们看到,这里用到了defer, defer的执行顺序是按照下面的规则的:

代码语言:javascript
复制
1. 多个defer的执行顺序为“后进先出”;

2. 所有函数在执行RET返回指令之前,都会先检查是否存在defer语句,若存在则先逆序调用defer语句进行收尾工作再退出返回;

3. 匿名返回值是在return执行时被声明,有名返回值则是在函数声明的同时被声明,因此在defer语句中只能访问有名返回值,而不能直接访问匿名返回值;

4. return其实应该包含前后两个步骤:第一步是给返回值赋值(若为有名返回值则直接赋值,若为匿名返回值则先声明再赋值);第二步是调用RET返回指令并传入返回值,而RET则会检查defer是否存在,若存在就先逆序插播defer语句,最后RET携带返回值退出函数;

因此,‍‍defer、return、返回值三者的执行顺序应该是:return最先给返回值赋值;接着defer开始执行一些收尾工作;最后RET指令携带返回值退出函数。

匿名返回值的情况:

代码语言:javascript
复制
package main

import (
    "fmt"
)

func main() {
    fmt.Println("a return:", a()) // 打印结果为 a return: 0
}

func a() int {
    var i int
    defer func() {
        i++
        fmt.Println("a defer2:", i) // 打印结果为 a defer2: 2
    }()
    defer func() {
        i++
        fmt.Println("a defer1:", i) // 打印结果为 a defer1: 1
    }()
    return i
}

有名返回值的情况:

代码语言:javascript
复制
package main

import (
    "fmt"
)

func main() {
    fmt.Println("b return:", b()) // 打印结果为 b return: 2
}

func b() (i int) {
    defer func() {
        i++
        fmt.Println("b defer2:", i) // 打印结果为 b defer2: 2
    }()
    defer func() {
        i++
        fmt.Println("b defer1:", i) // 打印结果为 b defer1: 1
    }()
    return i // 或者直接 return 效果相同
}

sync.WaitGroup and sync.Once

TODO

context.Context

TODO

sync.Pool

TODO

sync.Map

TODO

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
  • goroutine实现机制
  • golang垃圾回收
  • channel
  • panic, recover and defer
  • sync.WaitGroup and sync.Once
  • context.Context
  • sync.Pool
  • sync.Map
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档