今年行情回暖了

最近有朋友私聊问我说“阳哥,最近的行情是不是变好了?”

我没有正面回复他,因为其实这个问题根本没有答案。(很抱歉我的标题骗到了你 ovo)
我一直觉得讨论行情的“暖”和“寒”是没有意义的,举个很简单的例子:
当各个公司高喊“用工荒”,大量发放岗位的时候,照样有人找不到工作;当“降本增效”成为企业生存法则,各大企业都大量裁员的时候,照样有人能找到工作。
说到这里大家也应该懂我的意思了吧。
最近确实经常传来喜报,这里也给大家分享一下看看:

从简历已读不回,到加入组织后一个月内到手 offer

35 岁+程序员,一周到手俩 offer

Java 零基础学员实习上岸
为了找到工作,他们都是下了很大功夫的,不能因为一句“行情变暖”就忽视他们自己的努力,所有的东西都是靠自己争取到的。
换句话讲,行情变好还是变差,和我们没有一分钱关系,只管冲就完事了。
想都是问题,做才有答案!
Go 常见面试题分享
01 ❤channel 死锁的场景
当一个 channel 中没有数据,而直接读取时,会发生死锁:
解决方案是采用 select 语句,再 default 放默认处理方式:
当 channel 数据满了,再尝试写数据会造成死锁:
解决方法,采用 select
向一个关闭的 channel 写数据。
注意:一个已经关闭的 channel,只能读数据,不能写数据。
参考资料:Golang关于channel死锁情况的汇总以及解决方案
02 ❤对已经关闭的 chan 进行读写会怎么样?
读已经关闭的 chan 能一直读到东西,但是读到的内容根据通道内关闭前是否有元素而不同。
如果 chan 关闭前,buffer 内有元素还未读,会正确读到 chan 内的值,且返回的第二个 bool 值(是否读成功)为 true。
如果 chan 关闭前,buffer 内有元素已经被读完,chan 内无值,接下来所有接收的值都会非阻塞直接成功,返回 channel 元素的零值,但是第二个 bool 值一直为 false。
写已经关闭的 chan 会 panic。
03 说说 atomic 底层怎么实现的.
atomic 源码位于sync\atomic
。通过阅读源码可知,atomic 采用 CAS(CompareAndSwap)的方式实现的。所谓 CAS 就是使用了 CPU 中的原子性操作。在操作共享变量的时候,CAS 不需要对其进行加锁,而是通过类似于乐观锁的方式进行检测,总是假设被操作的值未曾改变(即与旧值相等),并一旦确认这个假设的真实性就立即进行值替换。本质上是不断占用 CPU 资源来避免加锁的开销。
04 channel 底层实现?是否线程安全。
channel 底层实现在 src/runtime/chan.go 中
channel 内部是一个循环链表。内部包含 buf, sendx, recvx, lock ,recvq, sendq 几个部分;
buf 是有缓冲的 channel 所特有的结构,用来存储缓存数据。是个循环链表;
sendx 和 recvx 用于记录 buf 这个循环链表中的发送或者接收的 index;
lock 是个互斥锁;
recvq 和 sendq 分别是接收(<-channel)或者发送(channel <- xxx)的 goroutine 抽象出来的结构体(sudog)的队列。是个双向链表。
channel 是线程安全的。
参考资料:Kitou:Golang 深度剖析 -- channel的底层实现
05 map 的底层实现。
源码位于 src\runtime\map.go 中。
go 的 map 和 C++map 不一样,底层实现是哈希表,包括两个部分:hmap 和 bucket。
里面最重要的是 buckets(桶),buckets 是一个指针,最终它指向的是一个结构体:
每个 bucket 固定包含 8 个 key 和 value(可以查看源码 bucketCnt=8).实现上面是一个固定的大小连续内存块,分成四部分:每个条目的状态,8 个 key 值,8 个 value 值,指向下个 bucket 的指针。
创建哈希表使用的是 makemap 函数.map 的一个关键点在于,哈希函数的选择。在程序启动时,会检测 cpu 是否支持 aes,如果支持,则使用 aes hash,否则使用 memhash。这是在函数 alginit() 中完成,位于路径:src/runtime/alg.go 下。
map 查找就是将 key 哈希后得到 64 位(64 位机)用最后 B 个比特位计算在哪个桶。在 bucket 中,从前往后找到第一个空位。这样,在查找某个 key 时,先找到对应的桶,再去遍历 bucket 中的 key。
关于 map 的查找和扩容可以参考map的用法到map底层实现分析。
06 select 的实现原理?
select 源码位于 src\runtime\select.go,最重要的 scase 数据结构为:
scase.c 为当前 case 语句所操作的 channel 指针,这也说明了一个 case 语句只能操作一个 channel。
scase.elem 表示缓冲区地址:
caseRecv : scase.elem 表示读出 channel 的数据存放地址;
caseSend : scase.elem 表示将要写入 channel 的数据存放地址;
select 的主要实现位于:select.go 函数:其主要功能如下:
锁定 scase 语句中所有的 channel
按照随机顺序检测 scase 中的 channel 是否 ready
2.1 如果 case 可读,则读取 channel 中数据,解锁所有的 channel,然后返回(case index, true)
2.2 如果 case 可写,则将数据写入 channel,解锁所有的 channel,然后返回(case index, false)
2.3 所有 case 都未 ready,则解锁所有的 channel,然后返回(default index, false)
所有 case 都未 ready,且没有 default 语句
3.1 将当前协程加入到所有 channel 的等待队列
3.2 当将协程转入阻塞,等待被唤醒
唤醒后返回 channel 对应的 case index
4.1 如果是读操作,解锁所有的 channel,然后返回(case index, true)
4.2 如果是写操作,解锁所有的 channel,然后返回(case index, false)
参考资料:Go select的使用和实现原理
07 go 的 interface 怎么实现的?
go interface 源码在runtime\iface.go
中。
go 的接口由两种类型实现 iface 和 eface。iface 是包含方法的接口,而 eface 不包含方法。
iface
对应的数据结构是(位于src\runtime\runtime2.go
):
可以简单理解为,tab 表示接口的具体结构类型,而 data 是接口的值。
itab
:
属性 interfacetype 类似于_type,其作用就是 interface 的公共描述,类似的还有 maptype、arraytype、chantype…其都是各个结构的公共描述,可以理解为一种外在的表现信息。interfaetype 和 type 唯一确定了接口类型,而 hash 用于查询和类型判断。fun 表示方法集。
eface
与 iface 基本一致,但是用_type 直接表示类型,这样的话就无法使用方法。
这里篇幅有限,深入讨论可以看:深入研究 Go interface 底层实现
08 go 的 reflect 底层实现
go reflect 源码位于 src\reflect\下面,作为一个库独立存在。反射是基于接口实现的。
Go 反射有三大法则:
反射从接口映射到反射对象;
反射从反射对象映射到接口值;
只有值可以修改(settable),才可以修改反射对象。
Go 反射基于上述三点实现。我们先从最核心的两个源文件入手 type.go 和 value.go.
type 用于获取当前值的类型。value 用于获取当前的值。
参考资料:The Laws of Reflection, 图解go反射实现原理
09 go GC 的原理知道吗?
如果需要从源码角度解释 GC,推荐阅读(非常详细,图文并茂)
欢迎关注 ❤
我们搞了一个免费的面试真题共享群,互通有无,一起刷题进步。
没准能让你能刷到自己意向公司的最新面试题呢。
感兴趣的朋友们可以加我微信:wangzhongyang1993,备注:面试群。
版权声明: 本文为 InfoQ 作者【王中阳Go】的原创文章。
原文链接:【http://xie.infoq.cn/article/184182fa31bed14f5cb7aed77】。文章转载请联系作者。
评论