这三个 Go 水平自测题,手写不出来还是先老实上班吧

开发 前端
利用 select+chan+defualt 的方式,很容易实现 TryLock、TryLockWithTimeout 的功能。具体来说就是,在 select 语句中,我们可以使用 default 实现 TryLock,再加一个 Timer 来实现 Timeout 的功能。

现在技术文章特别卷,啥啥底层都能给你分析的头头是道,但是分析的对不对要看作者水平,很有可能一个错,抄他的那些人也跟着错,因为我以前看源码的时候就经常感觉自己在两种状态下切换:懂了 / 娘咧漏看了,这个函数干啥的。

八股文这个事儿,其实也特别考验面试官,如果只会一味的问八股文,那也只能说你正巧比面试的人多看点八股,并不能彰显你多有水平,换个小年轻当面试官人家也能干啊。

最近跟以前的老同事聊,他说了个他特别爱问的面试题,我觉得还是挺有水平的,既能引导候选人循序渐进地展开思维,又能考察基础和动手能力。

PS:老哥是83年的,以前在一个广告平台公司 C++,Java,安卓啥的都干过,之前当过我领导,就是他让我刚来公司两星期就去会议室封闭参与用 Go 重构项目的,算是硬逼着我学了学 Go。

老哥说:Go 当初吸引人的地方不就是并发、Channel 这些嘛,其实用过后你会发现也就那样,宣传的有点过了,但是既然平时用 Go 开发,这块就一定得过关,那怎么并发和 Channel 都考察到呢?我一般会问:"Channel 和 并发掌握的熟练吧(一般没人会说不熟)那咱们先用 Channel 实现一个互斥锁",嘿,说你呢,实现一下。

我心想这题我面试别人用过,我背过……,还能难倒我:先初始化一个 capacity 等于 1 的 Channel,它的“空槽”代表锁,哪个协程能成功地把元素发送到这个 Channel,谁就获取了这把锁,给你上代码:

// 使用chan实现互斥锁
type Mutex struct {
ch chan struct{}
}

// 使用锁需要初始化
func NewMutex() *Mutex {
mu := &Mutex{make(chan struct{}, 1)}
mu.ch <- struct{}{}
return mu
}

// 请求锁,直到获取到
func (m *Mutex) Lock() {
<-m.ch
}

// 解锁
func (m *Mutex) Unlock() {
select {
case m.ch <- struct{}{}:
default:
panic("unlock of unlocked mutex")
}
}

老哥说:只要不是太混,这个道题都能答出来,那么接下来我一般会在这道题的基础上两个变种,首先让候选人再扩展一下给这个锁实现 TryLock 功能,TryLock 知道吧,你不是写过两年Java,这个用过吧,你在刚才的基础上实现一下。

我心想:我现在偶尔写Java 的时候都是把以前做的那些项目代码翻出来抄抄,我哪能记得这么清楚,不过这个不就是尝试获取锁,获取不到返回 false 嘛。

这里再给大家解释一下 TryLock 这个功能,下面这段话我从JavaDoc 里抄的:

tryLock() - 可轮询获取锁。如果成功,则返回 true;如果失败,则返回 false。也就是说,这个方法无论成败都会立即返回,获取不到锁(锁已被其他线程获取)时不会一直等待。

那这个也难不倒我啊,咱们学 Channel 的时候,都要学会利用 select+chan+default 的方式,避免程序阻塞住嘛,那我就套一下这个公式呗。(不过我老不写这个,语法我给忘了,多亏GoLand 提示我半天我才写出来,面试的时候一般在纸上写,咱们读者到时候记得贝贝哈)

// 尝试获取锁
func (m *Mutex) TryLock() bool {
select {
case <-m.ch:
return true
default:
}
return false
}

老哥:嗯,确实是这么个解法,不过你这写的也太慢了,算你过吧,其实我直接写估计也写不出来,天天开会手都生了。那好,这个变种如果能答出来,证明这个候选人基础应该还可以,Channel 使用这块应该都没啥问题,那这会儿我就会再扩展一下,让候选人再实现下 TryLock 的重载方法,就是可以设置超时时间那个重载函数,考察一下他定时器这块的知识过不过关,诶,我怎么把答案给说出来了,你懂我意思吧。

老哥的意思就是实现一下 TryWithTimeout,Java 里锁的 Try Lock 还有个重载方法:

tryLock(long, TimeUnit) - 可定时获取锁。和 tryLock() 类似,区别仅在于这个方法在获取不到锁时会等待一定的时间,在时间期限之内如果还获取不到锁,就返回 false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回 true。

因为 Go 里边没有重载方法这种机制咱们就只能写个 TryWithTimeout 方法啦,刚才说了用定时器能实现,不过这块我也忘了怎么用了…...只好默默打开的了浏览器搜索,最后实现答案版本如下:

// 加入一个超时的设置
func (m *Mutex) LockTimeout(timeout time.Duration) bool {
timer := time.NewTimer(timeout)
select {
case <-m.ch:
timer.Stop()
return true
case <-timer.C:
}
return false
}

最后老哥看了看:嗯,看着挺像那么回事的,今天家里领导有事,我得赶紧接二公主放学了,下回再聊吧。

平时我们实际应用时最好不要用 Channel 替代sync.Mutex,但是用 Channel 确实除了实现互斥锁的功能外,还能扩充出TryLocK和LockTimeout这些扩展功能。

利用 select+chan+defualt​ 的方式,很容易实现 TryLock、TryLockWithTimeout​ 的功能。具体来说就是,在 select​ 语句中,我们可以使用 default​ 实现 TryLock​,再加一个 Timer​ 来实现 Timeout 的功能。

责任编辑:武晓燕 来源: 网管叨bi叨
相关推荐

2023-10-26 07:29:06

mongodb十六进制ID

2010-08-17 22:20:20

HCNE网络工程师

2011-04-28 10:32:02

PHP自测

2010-08-17 22:51:02

HCNE网络工程师

2010-08-17 22:32:57

HCNE网络工程师

2023-02-07 13:51:11

SQLupdate语句

2021-02-06 12:28:53

低代码开发者数字化

2023-11-07 08:36:34

JavaScriptJS挑战

2021-04-16 09:17:39

机器学习人工智能AI

2018-04-28 11:03:58

2017-01-06 10:07:39

Linuxwindowsatime

2017-11-02 13:15:18

Linux

2020-05-29 10:18:58

python开发代码

2022-06-01 11:52:42

网站客户端网络

2022-03-30 14:19:36

云安全云计算风险

2015-09-23 09:48:04

2021-10-15 08:32:03

RocketMQ数据结构架构

2009-01-20 13:57:02

网络硬盘Google云计算

2024-10-14 08:01:09

阻塞死锁状态

2021-06-04 05:19:57

运营商用户投诉
点赞
收藏

51CTO技术栈公众号