不会飞的章鱼

熟能生巧,勤能补拙;念念不忘,必有回响。

提问学习带给我的收获

写以此文,记录我这三天(1.30-2.1)的收获和感悟。

上周把一个定时删除的服务的版本提交了,本以为在过年前的一周基本没什么事了。然而部门老哥告诉我,之前我负责的一个流媒体服务还存在两处bug

  • 不支持多用户同时拉取视频流;

  • 不支持重新连接;

所以这周我又忙着修改bug,然而在前两天,我还是束手无策,找不到应对方案,一筹莫展。

到了第三天老哥带我一步步熟悉整体流程,细致到每个函数,每个入参,以及每个重要的代码。不断的提问,不断的思考,再不断的提出新的问题,时间过得飞快,我也饿得快。不过最终还是蛮开心的,通过这两处bug,让我重新回顾了流媒体服务的整体工作流程,以及Go语言中对goroutinechannel机制,印象最深的是以下两点:

管道中的<-

简单来说就是这样子的:接受者<-发送者。

然而中间会多个管道,所以我借用Go语言圣经中的三处例子做解释

1
2
3
ch <- x  // x作为发送者发送给管道
x = <-ch // 管道作为发送者发送数据给接受者x
<-ch // 管道发送数据,没有接收者,丢弃,同时造成管道堵塞,等待接收者

所以我们可以具体化刚才说的发送接收流程,它应该为:接收者 <- 管道 <- 发送者。如果缺了接收者或发送者,都会造成管道堵塞。

互斥锁

举个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import "sync"

var (
mu sync.Mutex // guards balance
balance int
)

func Deposit(amount int) {
mu.Lock()
balance = balance + amount
mu.Unlock()
}

func Balance() int {
mu.Lock()
b := balance
mu.Unlock()
return b
}

先Lock锁住,再使用Unlock解锁。

如果Lock中再套一个Lock,就会造成死锁,需要将前一个Lock解开才行。

dlv调试流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1,./dlv debug xxxx(程序名)  ##启动dlv调试

2,r(restart)

3,c(continue)

4,b(break) ##打断点,可以打函数名,也可以打文件下对应的行号

5,n(next)或s(step) ##n按一次单步执行,后面只需一直按回车;遇到需要深究的函数按s进去查看
##如果碰到多线程,建议在线程内打个断点

6,bt(stack) ##查看堆栈

7,frame ##查看指定堆栈的内容

8,q(exit) ##退出调试

收获

  • 接触一个新东西,除了保证理解了流程,还要对每个函数的作用,影响范围都要了然于胸才行;

  • 流程这东西,光知道不行,最好的办法是自己画个流程图出来,一步步跟着代码走;

  • IDE有时会因为环境参数或内在bug而报错,所以推荐使用dlv(针对go)和gdb进行调试;

  • 多对自己提几个为什么,有助于理解技术的本质;

  • 心态放平和,坦然接受bug和不足,耐心寻求突破。

最后附一组测试成功的图片^_^

------ 本文结束------
如果本篇文章对你有帮助,可以给作者加个鸡腿~(*^__^*),感谢鼓励与支持!