如何从一个知识点获得源码层面的理解

来源:1-4 Go 程序是怎么跑起来的

快哉快哉

2021-05-16 00:48:58

经验已经告诉我执行下面的代码会阻塞主进程,然后会报错all goroutines are asleep - deadlock!

package main
func main()  {
	ch1 := make(chan string)
	ch1 <- "hello"
}

但是就是好奇,

  • 为什么非缓存队列只接受不发送就会阻塞?
  • 为什么主进程阻塞了,会报错all goroutines are asleep - deadlock!
    然后去源码搜索发现了个函数
// Check for deadlock situation.
// The check is based on number of running M's, if 0 -> deadlock.
// sched.lock must be held.
func checkdead() {
	// For -buildmode=c-shared or -buildmode=c-archive it's OK if
	// there are no running goroutines. The calling program is
	// assumed to be running.
	if islibrary || isarchive {
		return
	}
	...
	...
	...
	// There are no goroutines running, so we can look at the P's.
	for _, _p_ := range allp {
		if len(_p_.timers) > 0 {
			return
		}
	}

	getg().m.throwing = -1 // do not dump full stacks
	unlock(&sched.lock)    // unlock so that GODEBUG=scheddetail=1 doesn't hang
	throw("all goroutines are asleep - deadlock!")
}

但看到这些,毫无头绪从哪里看起,所以向问问曹大,当年读源码的经验?,指点一二。

写回答

2回答

Xargin

2021-05-16

首先要有一个宏观的理解,比如就是第一课讲的,知道整个 Go 底层就是一堆线程在不停地执行调度循环


然后在日常碰到问题的时候,再去看详细的实现。


比如你这里的 checkdead:

  • 搜索代码以后发现是在 sysmon 里,通过课程中讲的内容,知道 sysmon 是后台高优先级的一个监控线程,负责干什么事情

  • 然后看 checkdead 的注释,写的是检查是不是目前所有的线程都被阻塞住,那就大概了解这个流程了


宏观的认识,再去填充细节,我个人认为这样是比较好的


不过 checkdead 这个流程在我们写服务的时候基本上没啥用

1

Xargin

2021-05-16

第一节课讲了调度循环,了解了调度循环的流程,你对整体的运行框架应该就有了基础的理解


碰到问题的时候,像你这里说的 checkdead,你只要看看 checkdead 是在哪个阶段被调用的,它的功能是什么,再简单画一下图就理解了。


我看 checkdead 的时候,感觉他的注释写的还挺清楚的


中间我看看要不要讲讲怎么读 Go 的底层代码,里面其实有很多代码是可以忽略不读的,特别是一些 if raceenable,traceenable 或者你这里前几行 c-shared,library 之类的,可以先跳过的

2

Go高级工程师实战营

慕课网与 GoCN 社区官方联手打造,定义行业Go高级人才培养标准,4个月,快速晋升为P6+/D7级高级人才。

458 学习 · 266 问题

查看课程