写点什么

今年行情回暖了

作者:王中阳Go
  • 2025-06-03
    湖南
  • 本文字数:3190 字

    阅读完需:约 10 分钟

今年行情回暖了

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



我没有正面回复他,因为其实这个问题根本没有答案。(很抱歉我的标题骗到了你 ovo)


我一直觉得讨论行情的“暖”和“寒”是没有意义的,举个很简单的例子:


当各个公司高喊“用工荒”,大量发放岗位的时候,照样有人找不到工作;当“降本增效”成为企业生存法则,各大企业都大量裁员的时候,照样有人能找到工作


说到这里大家也应该懂我的意思了吧。


最近确实经常传来喜报,这里也给大家分享一下看看:



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



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



Java 零基础学员实习上岸


为了找到工作,他们都是下了很大功夫的,不能因为一句“行情变暖”就忽视他们自己的努力,所有的东西都是靠自己争取到的。


换句话讲,行情变好还是变差,和我们没有一分钱关系,只管冲就完事了。


想都是问题,做才有答案!

Go 常见面试题分享

01 ❤channel 死锁的场景

  • 当一个 channel 中没有数据,而直接读取时,会发生死锁:


q := make(chan int,2)<-q
复制代码


解决方案是采用 select 语句,再 default 放默认处理方式:


q := make(chan int,2)select{   case val:=<-q:   default:         ...
}
复制代码


  • 当 channel 数据满了,再尝试写数据会造成死锁:


q := make(chan int,2)q<-1q<-2q<-3
复制代码


解决方法,采用 select


func main() {        q := make(chan int, 2)        q <- 1        q <- 2        select {        case q <- 3:                fmt.Println("ok")        default:                fmt.Println("wrong")        }
}
复制代码


  • 向一个关闭的 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 资源来避免加锁的开销


参考资料:Go语言的原子操作atomic - 编程猎人

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 是一个指针,最终它指向的是一个结构体:


// A bucket for a Go map.type bmap struct {    tophash [bucketCnt]uint8}
复制代码


每个 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 数据结构为:


type scase struct {        c    *hchan         // chan        elem unsafe.Pointer // data element}
复制代码


scase.c 为当前 case 语句所操作的 channel 指针,这也说明了一个 case 语句只能操作一个 channel。


scase.elem 表示缓冲区地址:


  • caseRecv : scase.elem 表示读出 channel 的数据存放地址;

  • caseSend : scase.elem 表示将要写入 channel 的数据存放地址;


select 的主要实现位于:select.go 函数:其主要功能如下:


  1. 锁定 scase 语句中所有的 channel

  2. 按照随机顺序检测 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)


  1. 所有 case 都未 ready,且没有 default 语句


  • 3.1 将当前协程加入到所有 channel 的等待队列

  • 3.2 当将协程转入阻塞,等待被唤醒


  1. 唤醒后返回 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):


type iface struct {        tab  *itab        data unsafe.Pointer}
复制代码


可以简单理解为,tab 表示接口的具体结构类型,而 data 是接口的值。


  • itab


type itab struct {        inter *interfacetype //此属性用于定位到具体interface        _type *_type //此属性用于定位到具体interface        hash  uint32 // copy of _type.hash. Used for type switches.        _     [4]byte        fun   [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.}
复制代码


属性 interfacetype 类似于_type,其作用就是 interface 的公共描述,类似的还有 maptype、arraytype、chantype…其都是各个结构的公共描述,可以理解为一种外在的表现信息。interfaetype 和 type 唯一确定了接口类型,而 hash 用于查询和类型判断。fun 表示方法集。


  • eface


与 iface 基本一致,但是用_type 直接表示类型,这样的话就无法使用方法。


type eface struct {        _type *_type        data  unsafe.Pointer}
复制代码


这里篇幅有限,深入讨论可以看:深入研究 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,备注:面试群。

发布于: 刚刚阅读数: 3
用户头像

王中阳Go

关注

靠敲代码在北京买房的程序员 2022-10-09 加入

【微信】wangzhongyang1993【公众号】程序员升职加薪之旅【成就】InfoQ专家博主👍掘金签约作者👍B站&掘金&CSDN&思否等全平台账号:王中阳Go

评论

发布
暂无评论
今年行情回暖了_Go_王中阳Go_InfoQ写作社区