Golang
1. golang协程为什么比线程轻量?
- 初始堆栈大小:
每个系统线程的堆栈大小通常很大,通常在2MB左右。Go协程的初始堆栈大小远小得多,只有2KB,创建大量的goroutines需要的内存比创建相同数量的线程要少得多。 - 堆栈增长:
系统线程的堆栈大小通常是固定的,不能根据需求动态改变。而Go协程的堆栈是动态的,可以根据需求增长和缩小。当堆栈空间不够时,Go运行时可以自动增加其大小;当堆栈空间过大时,也可以自动缩小。 - 调度开销:
Go协程由Go运行时而非操作系统(OS)进行调度,因此调度开销更小。Go运行时使用M:N的调度模型,其中M个goroutine可以在N个OS线程上进行调度。这使得Go可以在用户级别实现轻量级的上下文切换,避免了频繁的系统调用和线程切换。
线程切换会触发用户态和内核态的切换,上下文切换的延迟取决于不同的因素,大概是50到100纳秒(ns)左右。考虑到硬件平均在每个核心上每纳秒执行12条指令,那么一次上下文切换可能会花费600到1200条指令的延迟时间。而Go将goroutine的调度维持在用户态,由GPM中的P(逻辑处理器)来完成调度任务,这个切换只涉及PC(程序计数器)、SP(堆栈指针)、DX(数据寄存器)三个寄存器的值的修改,相比线程的上下文切换(需要陷入内核模式以及16个寄存器的刷新)要轻量得多。 - 创建和销毁开销:
创建和销毁goroutine的开销小于线程,这是因为Go语言直接在语言级别支持协程,而线程是依赖操作系统来管理的。
2.协程泄漏的原因
- 协程没有正确关闭
- 协程阻塞或死循环
- 协程竞争条件
- 通道操作未处理
- 系统调用阻塞
- 资源锁定
- 循环等待
- 无限死循环
- I/O操作阻塞
- 未初始化的通道
互斥锁
锁(Mutex)是一种常用的同步机制,用于在多线程或多协程环境中保护共享资源,以防止多个线程或协程同时访问该资源,从而导致数据竞争或不一致性。在Go语言中,互斥锁是通过sync.Mutex类型提供的。
工作原理
互斥锁的基本工作原理是:当一个线程或协程持有锁时,其他尝试获取该锁的线程或协程将被阻塞,直到锁被释放为止。这样,持有锁的线程或协程可以独占访问共享资源,完成必要的操作,然后释放锁,允许其他线程或协程继续执行。
golang并发机制
Goroutine
Channel
Select语句:处理多个channel的操作
同步原语:Mutex(互斥锁)、WaitGroup和Once
Context包
golang中make 和 new 的区别
golang中map 是线程安全的吗
不安全,可以使用如下:
读写锁(RWMutex): sync.RWMutex.Lock(),sync.RWMutex.Unlock(),sync.RWMutex.RLock(),sync.RWMutex.RUnlock()
分片加锁:将数据分成多个片段,每个片段有自己的锁,从而减少锁的粒度,提高并发性能的方法。nc.Map:
Store(key, value interface{})储键值对。
Load(key interface{}) (value interface{}, ok bool):加载键对应的值。
Delete(key interface{}):删除键值对。
Range(f func(key, value interface{}) bool):遍历所有键值对。
map 的扩容规则
计算负载因子、计算新容量、重新分配内存、复制元素、更新指针、渐进式扩容
Golang中数组和切片的区别
数组和切片都是用来存储一组相同类型的元素的数据结构,但是它们有以下几点区别:
1.定义方式不同:数组的定义需要指定数组长度,而切片则不需要。
2.内存管理方式不同:数组是固定长度的,一旦定义就不能更改长度。而切片可以动态增加或缩小长度,因此切片需要在堆上分配内存,而数组则是在栈上分配内存。
3.传递方式不同:数组作为参数传递时,会进行值拷贝,即传递的是数组的副本;而切片作为参数传递时,传递的是指向底层数组的指针,因此对切片的修改会影响底层数组。
4.初始化方式不同:数组可以通过直接赋值初始化,例如a := [3]int{1,2,3};而切片通常通过**make()**函数初始化,例如b := make([]int, 3)。
总之,数组和切片在使用时,需要根据具体的场景选择合适的数据结构。如果需要动态增加或缩小长度,应该使用切片;如果需要固定长度的数据结构,可以使用数组。
原文地址:https://blog.csdn.net/weixin_42094245/article/details/143691585
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!