热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

字节跳动Go语言面试会问哪些问题?

1、Mutex几种状态mutexLocked—表示互斥锁的锁定状态;mutexWoken—表示从正常模式被从唤醒;mutexStarving—当前的互斥锁进入饥饿状态;waiter

f71b093137004d2994c493b59b70280a.png

1、Mutex 几种状态

  • mutexLocked — 表示互斥锁的锁定状态;

  • mutexWoken — 表示从正常模式被从唤醒;

  • mutexStarving — 当前的互斥锁进入饥饿状态;

  • waitersCount — 当前互斥锁上等待的 Goroutine 个数;

2、Mutex 正常模式和饥饿模式正常模式(非公平锁)

正常模式下,所有等待锁的 goroutine 按照 FIFO(先进先出)顺序等待。唤醒的goroutine 不会直接拥有锁,而是会和新请求锁的 goroutine 竞争锁的拥有。新请求锁的 goroutine 具有优势:它正在 CPU 上执行,而且可能有好几个,所 以刚刚唤醒的 goroutine 有很大可能在锁竞争中失败。在这种情况下,这个被 唤醒的 goroutine 会加入到等待队列的前面。如果一个等待的 goroutine 超过1ms 没有获取锁,那么它将会把锁转变为饥饿模式。

饥饿模式(公平锁)

为了解决了等待 G 队列的长尾问题
饥饿模式下,直接由 unlock 把锁交给等待队列中排在第一位的 G(队头),同 时,饥饿模式下,新进来的 G 不会参与抢锁也不会进入自旋状态,会直接进入 等待队列的尾部,这样很好的解决了老的 g 一直抢不到锁的场景。饥饿模式的触发条件,当一个 G 等待锁时间超过 1 毫秒时,或者当前队列只剩 下一个 g 的时候,Mutex 切换到饥饿模式。

总结

对于两种模式,正常模式下的性能是最好的,goroutine 可以连续多次获取 锁,饥饿模式解决了取锁公平的问题,但是性能会下降,其实是性能和公平的 一个平衡模式。

3、Mutex 允许自旋的条件

1 锁已被占用,并且锁不处于饥饿模式。
2 积累的自旋次数小于最大自旋次数(active_spin=4)。3 cpu 核数大于 1。
4 有空闲的 P。
5 当前 goroutine 所挂载的 P 下,本地待运行队列为空。

ecb533a8d7e466e56f686259fde3a6d1.png

4、RWMutex 实现

通过记录 readerCount 读锁的数量来进行控制&#xff0c;当有一个写锁的时候&#xff0c;会将读 锁数量设置为负数 1<<30。目的是让新进入的读锁等待写锁之后释放通知读 锁。同样的写锁也会等等待之前的读锁都释放完毕&#xff0c;才会开始进行后续的操 作。而等写锁释放完之后&#xff0c;会将值重新加上 1<<30, 并通知刚才新进入的读锁(rw.readerSem)&#xff0c;两者互相限制。

5、RWMutex 注意事项

  • RWMutex 是单写多读锁&#xff0c;该锁可以加多个读锁或者一个写锁

  • 读锁占用的情况下会阻止写&#xff0c;不会阻止读&#xff0c;多个goroutine 可以同时获取读锁

  • 写锁会阻止其他 goroutine(无论读和写)进来&#xff0c;整个锁由该 goroutine独占

  • 适用于读多写少的场景

  • RWMutex 类型变量的零值是一个未锁定状态的互斥锁。

  • RWMutex 在首次被使用之后就不能再被拷贝。

  • RWMutex 的读锁或写锁在未锁定状态&#xff0c;解锁操作都会引发 panic。

  • RWMutex 的一个写锁 Lock 去锁定临界区的共享资源&#xff0c;如果临界区的共享资源已被(读锁或写锁)锁定&#xff0c;这个写锁操作的 goroutine 将被阻塞直到解锁。

  • RWMutex 的读锁不要用于递归调用&#xff0c;比较容易产生死锁。

  • RWMutex 的锁定状态与特定的 goroutine 没有关联。一个 goroutine 可以 RLock(Lock)&#xff0c;另一个 goroutine 可以 RUnlock(Unlock)。

  • 写锁被解锁后&#xff0c;所有因操作锁定读锁而被阻塞的 goroutine 会被唤醒&#xff0c;并都可以成功锁定读锁。

  • 读锁被解锁后&#xff0c;在没有被其他读锁锁定的前提下&#xff0c;所有因操作锁定写锁而被阻塞的 goroutine&#xff0c;其中等待时间最长的一个 goroutine 会被唤醒。

6、Cond 是什么

Cond 实现了一种条件变量&#xff0c;可以使用在多个 Reader 等待共享资源 ready 的场 景(如果只有一读一写&#xff0c;一个锁或者 channel 就搞定了)
每个 Cond 都会关联一个 Lock(*sync.Mutex or *sync.RWMutex)&#xff0c;当修改条 件或者调用 Wait 方法时&#xff0c;必须加锁&#xff0c;保护 condition。

7、Broadcast 和 Signal 区别

func (c *Cond) Broadcast()

Broadcast 会唤醒所有等待 c 的 goroutine。

调用 Broadcast 的时候&#xff0c;可以加锁&#xff0c;也可以不加锁。

func (c *Cond) Signal()

Signal 只唤醒 1 个等待 c 的 goroutine。
调用 Signal 的时候&#xff0c;可以加锁&#xff0c;也可以不加锁。

8、Cond 中 Wait 使用

func (c *Cond) Wait()

Wait()会自动释放 c.L&#xff0c;并挂起调用者的 goroutine。之后恢复执行&#xff0c;Wait()会 在返回时对 c.L 加锁。
除非被 Signal 或者 Broadcast 唤醒&#xff0c;否则 Wait()不会返回。
由于 Wait()第一次恢复时&#xff0c;C.L 并没有加锁&#xff0c;所以当 Wait 返回时&#xff0c;调用者通常 并不能假设条件为真。

取而代之的是, 调用者应该在循环中调用 Wait。(简单来说&#xff0c;只要想使用condition&#xff0c;就必须加锁。)

for !condition() {c.Wait()
}
... make use of condition ...
c.L.Unlock()
c.L.Lock()

9、WaitGroup 用法

一个 WaitGroup 对象可以等待一组协程结束。使用方法是:
1. main 协程通过调用 wg.Add(delta int) 设置 worker 协程的个数&#xff0c;然后创 建 worker 协程;
2.worker 协程执行结束以后&#xff0c;都要调用 wg.Done();

3.main 协程调用 wg.Wait() 且被 block&#xff0c;直到所有 worker 协程全部执行结束 后返回。

10、WaitGroup 实现原理

  • WaitGroup 主要维护了 2 个计数器&#xff0c;一个是请求计数器 v&#xff0c;一个是等待计数 器 w&#xff0c;二者组成一个 64bit 的值&#xff0c;请求计数器占高 32bit&#xff0c;等待计数器占低32bit。

  • 每次Add执行&#xff0c;请求计数器v加1&#xff0c;Done方法执行&#xff0c;请求计数器减1&#xff0c;v为0 时通过信号量唤醒 Wait()。

11、什么是 sync.Once

  • Once 可以用来执行且仅仅执行一次动作&#xff0c;常常用于单例对象的初始化场 景。

  • Once 常常用来初始化单例资源&#xff0c;或者并发访问只需初始化一次的共享资 源&#xff0c;或者在测试的时候初始化一次测试资源。

  • sync.Once 只暴露了一个方法 Do&#xff0c;你可以多次调用 Do 方法&#xff0c;但是只有第 一次调用 Do 方法时 f 参数才会执行&#xff0c;这里的 f 是一个无参数无返回值 的函数。

12、什么操作叫做原子操作

一个或者多个操作在 CPU 执行过程中不被中断的特性&#xff0c;称为原子性(atomicity)。这些操作对外表现成一个不可分割的整体&#xff0c;他们要么都执行&#xff0c;要 么都不执行&#xff0c;外界不会看到他们只执行到一半的状态。而在现实世界中&#xff0c;CPU不可能不中断的执行一系列操作&#xff0c;但如果我们在执行多个操作时&#xff0c;能让他们的 中间状态对外不可见&#xff0c;那我们就可以宣城他们拥有了“不可分割”的原子性。

在 Go 中&#xff0c;一条普通的赋值语句其实不是一个原子操作。列如&#xff0c;在 32 位机器上 写 int64 类型的变量就会有中间状态&#xff0c;因为他会被拆成两次写操作(MOV)——写 低32位和写高32位。

13、原子操作和锁的区别

原子操作由底层硬件支持&#xff0c;而锁则由操作系统的调度器实现。锁应当用来保护 一段逻辑&#xff0c;对于一个变量更新的保护&#xff0c;原子操作通常会更有效率&#xff0c;并且更能利 用计算机多核的优势&#xff0c;如果要更新的是一个复合对象&#xff0c;则应当使用atomic.Value 封装好的实现。

14、什么是 CAS

CAS 的全称为 Compare And Swap&#xff0c;直译就是比较交换。是一条 CPU 的原子指 令&#xff0c;其作用是让 CPU 先进行比较两个值是否相等&#xff0c;然后原子地更新某个位置的 值&#xff0c;其实现方式是给予硬件平台的汇编指令&#xff0c;在 intel 的 CPU 中&#xff0c;使用的cmpxchg 指令&#xff0c;就是说 CAS 是靠硬件实现的&#xff0c;从而在硬件层面提升效率。

简述过程是这样:

假设包含 3 个参数内存位置(V)、预期原值(A)和新值(B)。V 表示要更新变量的 值&#xff0c;E 表示预期值&#xff0c;N 表示新值。仅当 V 值等于 E 值时&#xff0c;才会将 V 的值设为 N&#xff0c; 如果 V 值和 E 值不同&#xff0c;则说明已经有其他线程在做更新&#xff0c;则当前线程什么都不 做&#xff0c;最后 CAS 返回当前 V 的真实值。CAS 操作时抱着乐观的态度进行的&#xff0c;它总 是认为自己可以成功完成操作。基于这样的原理&#xff0c;CAS 操作即使没有锁&#xff0c;也可 以发现其他线程对于当前线程的干扰。

15、sync.Pool 有什么用

对于很多需要重复分配、回收内存的地方&#xff0c;sync.Pool 是一个很好的选择。频 繁地分配、回收内存会给 GC 带来一定的负担&#xff0c;严重的时候会引起 CPU 的毛 刺&#xff0c;而 sync.Pool 可以将暂时不用的对象缓存起来&#xff0c;待下次需要的时候直接 使用&#xff0c;不用再次经过内存分配&#xff0c;复用对象的内存&#xff0c;减轻 GC 的压力&#xff0c;提升系统 的性能。

最后&#xff1a;

点击下方名片链接&#xff0c;关注 「码农编程进阶笔记 」微信公众号&#xff0c;在微信聊天对话框回复「go语言实战」「goweb编程」或者直接长按左下图海报中的二维码&#xff0c;可获取最新golang电子书和视频资源


推荐阅读
  • 面试题总结_2019年全网最热门的123个Java并发面试题总结
    面试题总结_2019年全网最热门的123个Java并发面试题总结 ... [详细]
  • 浅析python实现布隆过滤器及Redis中的缓存穿透原理_python
    本文带你了解了位图的实现,布隆过滤器的原理及Python中的使用,以及布隆过滤器如何应对Redis中的缓存穿透,相信你对布隆过滤 ... [详细]
  • 随着全球对青少年编程能力的重视,中国也在积极培养未来的科技人才。本文通过具体的数学问题,展示如何使用 C++ 编程来解决这些问题,帮助青少年提高编程兴趣和能力。 ... [详细]
  • PBO(PixelBufferObject),将像素数据存储在显存中。优点:1、快速的像素数据传递,它采用了一种叫DMA(DirectM ... [详细]
  • 使用 Jupyter Notebook 实现 Markdown 编写与代码运行
    Jupyter Notebook 是一个开源的基于网页的应用程序,允许用户在同一文档中编写 Markdown 文本和运行多种编程语言的代码,并实时查看运行结果。 ... [详细]
  • Bootstrap 插件使用指南
    本文详细介绍了如何在 Web 前端开发中使用 Bootstrap 插件,包括自动触发插件的方法、插件的引用方式以及具体的实例。 ... [详细]
  • 本文整理了一份基础的嵌入式Linux工程师笔试题,涵盖填空题、编程题和简答题,旨在帮助考生更好地准备考试。 ... [详细]
  • 小程序的授权和登陆
    小程序的授权和登陆 ... [详细]
  • Cookie学习小结
    Cookie学习小结 ... [详细]
  • 本文将介绍如何在混合开发(Hybrid)应用中实现Native与HTML5的交互,包括基本概念、学习目标以及具体的实现步骤。 ... [详细]
  • 本文详细介绍了Java代码分层的基本概念和常见分层模式,特别是MVC模式。同时探讨了不同项目需求下的分层策略,帮助读者更好地理解和应用Java分层思想。 ... [详细]
  • 近期,微信公众平台上的HTML5游戏引起了广泛讨论,预示着HTML5游戏将迎来新的发展机遇。磊友科技的赵霏,作为一名HTML5技术的倡导者,分享了他在微信平台上开发HTML5游戏的经验和见解。 ... [详细]
  • 本文详细介绍了Java反射机制的基本概念、获取Class对象的方法、反射的主要功能及其在实际开发中的应用。通过具体示例,帮助读者更好地理解和使用Java反射。 ... [详细]
  • Python 数据可视化实战指南
    本文详细介绍如何使用 Python 进行数据可视化,涵盖从环境搭建到具体实例的全过程。 ... [详细]
  • 本文总结了一些开发中常见的问题及其解决方案,包括特性过滤器的使用、NuGet程序集版本冲突、线程存储、溢出检查、ThreadPool的最大线程数设置、Redis使用中的问题以及Task.Result和Task.GetAwaiter().GetResult()的区别。 ... [详细]
author-avatar
2502885590_296
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有