网上找了一些golang的面试题并且自己写上了自己的解答。
  1. Go里有哪些数据结构是并发安全的?int类型是并发安全的吗?
    int类型并不安全,对它的加减操作编译成底层机器码是有多条的,并发安全需要它的操作是原子的,os实现原子操作的方式有硬件支持cas,锁总线等方式。32位赋值操作是原子,64位赋值操作则是非原子的。
    sync包内有并发安全的数据结构: Mutex/RWMutex/WaitGroup/Map/tomic/Channel。
  2. Go如何实现一个单例模式?
    全局变量+init函数;sync.Once封装实例初始化过程,保证只执行一次;双重检查懒加载。
  3. sync.Once是如何实现的,如何不使用sync.Once实现单例模式?
    Once维护了一个原子整数和一个锁,调用过Do函数整数会+1,只有整数为0的时候才会执行函数。每次调用Do都是加锁执行的。可以用双重检查来实现单例。
  4. Go语言里的map是并发安全的吗?
    不是,想使用安全的map要用sync.Map。
  5. 如果要实现一个并发安全的map,该怎么做?
    分桶加锁,参考 https://github.com/seymourtang/concurrency-map/blob/master/map.go
  6. Slice与数组的区别?Slice的底层实现?复制和传值时,是深拷贝还是浅拷贝?
    slice本质上是维护了数组的一个视图,结构体内是数组指针、len和cap。赋值和传值都是浅拷贝。
  7. sync.map的底层实现?
    syncMap有一个readmap和dirtymap,读值的时候先去readmap查,查不到再穿透到dirtymap。dirtyMap是写值会使用。思想就是空间换时间、读写分离。syncMap适合读多写少的场景。需要读写并发的map可以自己实现一个分桶加锁的map。
  8. sync.mutex的底层实现?
    mutex是互斥锁,有normal和starvation两种模式,不支持重入。mutex维护了两个变量,state锁状态和sema信号量。state维护了waiter、woken、starving、locked的状态。locked=1就是加锁状态。waiter就是阻塞的协程数量。协程在normal状态加锁时会自旋一段时间再获取锁,标记woken=1,此时解锁协程只需要释放锁不需要去唤醒协程。如果阻塞的协程被唤醒后发现锁被抢走了,则再次阻塞,判定阻塞时常超过1ms则会标记starving,不会启动自旋过程,下次解锁肯定会把锁交给阻塞协程。
  9. 如何判断一个结构体是否实现了某接口?
    使用断言_, ok := someValue.(SomeInterface)
  10. channel底层实现?什么时候会panic?
    channel底层实现就是一个循环数组实现的消息队列,用来保存缓冲的消息。还有发送者和接受者的阻塞队列。panic的情况:未初始化时关闭、重复关闭、关闭后发送、发送时关闭。
    参考: https://zhuanlan.zhihu.com/p/544363104
  11. 介绍GMP模型,相比于正常的协程-线程-进程调度,有哪些优点和缺点?
    G(Goroutine)M(Mechine)P(Processor),调度器会启动M(核数)个线程,每个线程维护一个Processor,P维护一个Goroutine队列。还有一个公共Goroutine队列。当P的工作队列空了之后会拿别的P的Goroutine来运行。GMP模型比传统的协程优点在于自动调度,透明,轻量级。比传统协程实现复杂,传统协程需要手动管理。

求醉的夕阳
4 声望0 粉丝