golang

本文探讨Go语言中的Map实现,强调其内部的存储桶和链表解决冲突的方式,以及动态扩容机制。同时,介绍了Go的GMP模型,包括G(Goroutine)、M(M线程)和P(逻辑处理器)的概念。文章还涵盖了Go的并发原理解析,如通道(Channel)、WaitGroup和 Panic/Recover的使用,以及反射和依赖注入等高级特性。
摘要由CSDN通过智能技术生成
前言:语言特性没必要一下学完,而应该学习常用的,剩下的用的时候再查就行,语言类的书的定位应该是工具书,而不是学习的书。

go的map实现?
将键映射到存储桶,链来解决冲突。需要使用sync.map或加锁控制互斥,动态扩容。
怎么判断hashmap是否该扩容?键值对数量(是当前map中存储的键值对的数量)/存储桶数组长度(是当前map底层数组的长度)= 负载因子,负载因子超过一定的阈值时,就会触发map的扩容操作,默认为6.5。
不扩容,链就会很长,扩容的话桶会变多 ,hash算法也会改变,导致之前一个桶的会分到两个桶中去,采用增量式扩容的方式,通过逐步复制原有存储桶数组的内容到新的存储桶数组中来实现扩容,将复制操作分摊到多次操作中,以减少单次扩容的开销。

go的gmp模型?
GMP模型的设计目标是在多核处理器上实现高效的并发和并行操作。具体内容参考brpc那篇博客。
G 协程
M 线程
P 逻辑处理器

golang与c类似,静态,但有GC。

:=定义变量 或 var a int。

defer语句会延迟到外层函数返回后再执行。

切片:前包含后不包含,可用make创建切片。
 
栈逃逸:任何时候如果一个函数的栈帧上的值在函数外共享,这个值将会在分配在堆上。

闭包特性:
func fa(a int) func(i int) int{
	return func(i int) int{
		println(&a,a)
		a=a+i
		return a
	}
}
f:=fa(1),g:=fa(1),f(1) 为1 ,f(1) 为2,g(1) 为1 ,g(1) 为2。

map[K]V
for k,v:=range map{
} 

发生panic后,会逐层向上返回,所以一般把recover写在defer中。

接口: 
type A interface{
	Print()
}
然后定义一个struct 实现它的Print()函数,就可以var i A=struct实例化的对象,i.Print()。

goroutine,协程并行,可以先定义一个函数func A(),然后在main中go A(),main函数本身就是一个goroutine。
	
goroutine间用chan(通道)通信,使用fifo先进先出。
make(chan datatype),无缓冲,一般用来同步goroutine    
1: c  <- struct{}{}  (此时1会阻塞至2执行)  
2<-c;size int 即用户指定的channel缓冲区大小,不指定则为0。由于发送和接收操作都是阻塞式的,所以这个程序会一直等待直到发送和接收操作匹配,才能正常结束。

make(chan datatype,10)有缓冲,(chan满了再放会阻塞,空的去拿会阻塞)。
管道的关闭:使用内置函数close可以关闭channel,当channel关闭后,就不能再向channel写数据了,但是仍然可以从该channel读取数据。

同步:
var wg sync.WaitGroup
wg.Add(1) 
defer wg.Done()//会 -1
wg.Wait()

select可监听多个chan,select是Golang在语言层面提供的多路IO复用的机制
for {
	select{
		case ch<-0:
		处理1
		case ch<-1:
		处理2
		default:
		处理3
	} // 当一个都不满足时会走default,无default会阻塞,0、1的写入是随机的。
}

反射:  
s:=Student{}
rt:=reflect.Typeof(s) ,传Stuent的结构体也可以,不是只能传实例化的对象。
rt.FieldByname("Name")

依赖注入和控制反转
inj := inject.New()
inj.Map("tom")  // 注入 tom的生命不是我控制了
inj.Invoke(函数名) // 这个函数有个参数是string

goroutine中使用recover,解决协程中出现panic,导致程序崩溃问题。
func test() {
   // 这里使用defer + recover
   defer func() {
      // 捕获test的panic
      if err := recover(); err != nil {
         fmt.Println("test() 发生错误  ", err)
      }
   }()
   var myMap map[int]string
   myMap[0] = "golang" // error
}

select本身不带循环,需要外层的for,default通常不用,会产生忙轮询 写个sleep感觉可以
for {
    select {
        case <-chan1:
        // ......
        case chan2<-1:
        // ......
        default:
        //都没成功进入......
    }
}

有时候会出现协程阻塞的情况,那么我们如何避免这个情况?可以使用select来设置超时
func main() {
   c := make(chan int)
   o := make(chan bool)
   go func() {
      for {
         select {
         case v:= <-c:
            fmt.Println(v)
         // 5秒钟自动关闭,避免长时间超时
         case < - time.After(5 * time.Second):
            fmt.Println("timeout")
            o<-true
            break
         }
      }
   }()
  // 有值就主协程走,主协程走完就都没了
   <-o
   fmt.Println("程序结束")
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

成长是自己的事

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

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

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

打赏作者

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

抵扣说明:

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

余额充值