Go 每日一题(一)

Goroutine和线程的区别?

   从调度上看, goroutine的调度开销远远⼩于线程调度开销。 OS的线程由OS内核调度,每隔⼏毫秒,⼀ 个硬件时钟中断发到CPU ,CPU调⽤⼀个调度器内核函数。这个函数暂 停当前正在运⾏的线程,把他的 寄存器信息保存到内存中,查看线程列表并决定接下来运⾏哪⼀个线程,再从内存 中恢复线程的注册表 信息,最后继续执⾏选中的线程。这种线程切换需要⼀个完整的上下⽂切换:即保存⼀个线程 的状态到 内存,再恢复另外⼀个线程的状态,最后更新调度器的数据结构。某种意义上,这种操作还是很慢的。
   Go运⾏的时候包涵⼀个⾃⼰的调度器,这个调度器使⽤⼀个称为⼀个M:N调度技术, m个goroutine到n 个os线程 (可以⽤GOMAXPROCS来控制n的数量), Go的调度器不是由硬件时钟来定期触发的,⽽是 由特定的go语⾔结构 来触发的,他不需要切换到内核语境,所以调度⼀个goroutine⽐调度⼀个线程的 成本低很多。 从栈空间上, goroutine的栈空间更加动态灵活。 每个OS的线程都有⼀个固定⼤⼩的栈内 存,通常是2MB,栈内存⽤于保存在其他函数调⽤期间哪些正在执⾏或者 临时暂停的函数的局部变量。
这个固定的栈⼤⼩,如果对于goroutine来说,可能是⼀种巨⼤的浪费。作为对⽐ goroutine在⽣命周期 开始只有⼀个很⼩的栈,典型情况是2KB, 在go程序中,⼀次创建⼗万左右的goroutine也不 罕⻅
   (2KB*100,000=200MB)。⽽且goroutine的栈不是固定⼤⼩,它可以按需增⼤和缩⼩,最⼤限制可以 到 1GB。 goroutine没有⼀个特定的标识。 在⼤部分⽀持多线程的操作系统和编程语⾔中,线程有⼀个 独特的标识,通常是⼀个整数或者指针,这个特性可以 让我们构建⼀个线程的局部存储,本质是⼀个全 局的map,以线程的标识作为键,这样每个线程可以独⽴使⽤这个 map存储和获取值,不受其他线程⼲ 扰。 goroutine中没有可供程序员访问的标识,原因是⼀种纯函数的理念,不希望滥⽤线程局部存储导致 ⼀个不健康的 超距作⽤,即函数的⾏为不仅取决于它的参数,还取决于运⾏它的线程标识。

Go 语言是如何实现切片扩容的?

func main() {
arr := make([]int, 0)
for i := 0; i < 2000; i++ {
fmt.Println("len 为", len(arr), "cap 为", cap(arr))
arr = append(arr, i)
}
}

我们可以看下结果
依次是 0,1,2,4,8,16,32,64,128,256,512,1024
但到了 1024 之后,就变成了 1024,1280,1696,2304
每次都是扩容了四分之一左右

看下面代码的 defer 的执行顺序是什么 ? defer 的作用和 特 点是什么?

defer 的作用是:

  1. 你只需要在调用普通函数或方法前加上关键字 defer,就完成了 defer 所需要 的语法。 当 defer 语句被执行时,跟在 defer 后面的函数会被延迟执行。

  2. 直到 包含该 defer 语句的函数执行完毕时, defer 后的函数才会被执行,不论包含 defer 语句的函 数是通过 return 正常结束,还是由于 panic 导致的异常结束。

  3. 你可以在一个函数中执行多条 defer 语句,它们的执行顺序与声明顺序相反。
    defer 的常用场景:

  4. defer 语句经常被用于处理成对的操作,如打开、关闭、连接、断开连接、 加锁、释放锁。

  5. 通过 defer 机制,不论函数逻辑多复杂,都能保证在任何执行路径下,资 源被释放。

  6. 释放资源的 defer 应该直接跟在请求资源的语句后

Golang Slice 的底层实现

   切片是基于数组实现的,它的底层是数组,它自己本身非常小,可以理解为对 底层数组的抽象。因为基 于数组实现,所以它的底层的内存是连续分配的,效 率非常高,还可以通过索引获得数据
切片本身并不是动态数组或者数组指针。它内部实现的数据结构通过指针引用 底层数组,设定相关属性 将数据读写操作限定在指定的区域内。切片本身是一 个只读对象,其工作机制类似数组指针的一种封
装。 切片对象非常小,是因为它是只有 3 个字段的数据结构:

  1. 指向底层数组的指针
  2. 切片的长度
  3. 切片的容量

Golang Slice 的扩容机制,有什么注意点?

Go 中切片扩容的策略是这样的:

  1. 首先判断,如果新申请容量大于 2 倍的旧容量,最终容量就是新申请的容 量
  2. 否则判断,如果旧切片的长度小于 1024,则最终容量就是旧容量的两倍
  3. 否则判断,如果旧切片长度大于等于 1024,则最终容量从旧容量开始循环 增加原来的 1/4, 直到最 终容量大于等于新申请的容量
  4. 如果最终容量计算值溢出,则最终容量就是新申请容量
  • 30
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Go 的学习之路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值