《Go 并发编程实战》读书笔记

GO 并发编程实战

第 1 章:初始 Go 语言

  • 访问控制级别:公开、包级私有之外,还有一种——模块级私有。通过吧名称首字母大写的程序实体放入 internal 代码包实现的。
  • 多编程范式:Go 支持函数式编程。函数类型为第一等类型,可以方便地传递和赋值。此外,它还支持面向对象编程,有接口类型与实现类型的概念,但用嵌入替代了继承。
  • Go 命令:
    • clean。用于清除因执行其他 go 命令而遗留下来的临时目录和文件。
    • doc。用于显示Go语言代码包以及程序实体的文档。
    • fix。用于修正指定代码包的源码文件中包含的过时语法和代码调用。这使得我们在升级Go语言版本时,可以非常方便地同步升级程序。
    • fmt。用于格式化指定代码包中的Go源码文件。实际上,它是通过执行gofmt命令来实现功能的。
    • generate。用于识别指定代码包中源码文件中的go:generate注释,并执行其携带的任意命令。该命令独立于 Go 语言标准的编译和安装体系。如果你有需要解析的go:generate注释,就单独运行它。这个命令非常有用,我常用它自动生成或改动 Go 源码文件。
    • list。用于显示指定代码包的信息,它可谓是代码包分析的一大便捷工具。利用 go 语言标准库代码包 text/template 中规定的模板语法,你可以非常灵活地控制输出信息。
    • tool。用于运行 Go 语言的特殊工具。
    • vet。用于检查指定代码包中的 Go 语言源码,并报告发现可疑代码问题。该命令提供了除编译以外的又一个程序检查方法,可用于找到程序中的潜在错误。
  • 命令额外标记:
    • -a:用于强调重新编译所有涉及的 Go 语言代码包。
    • -n:使命令仅打印其执行过程中用到的所有命令,而不真正执行它们。
    • -race:用于检测程序中的数据竞争问题。
    • -v:用于打印命令执行过程中涉及的代码包。
    • -work:用于打印命令执行时生成和使用的临时工作目录的名字,且命令执行完成后不删除它们。
    • -x:使命令仅打印其执行过程中用到的所有命令,同时执行它们。
  • pprof:用于以交互的方式访问一些性能概要文件。命令将会分析给定的概要文件并根据要求提供高可读性的输出信息。这个工具可以分析的概要文件包括 CPU 概要文件、内存概要文件和程序阻塞概要文件。这些包含 Go 程序运行信息的概要文件,可以通过标准库代码包 runtime 和 runtime/pprof 中的程序来生成。
  • trace:用于读取 Go 程序踪迹文件,并以图形化的方式展示出来。它能够让我们深入了解 Go 程序在运行过程中的内部情况。比如,当前进程中堆的大小及使用情况。又比如,程序中的多个 goroutine 是怎样被调度的,以及它们在某个时刻被调度的原因。Go 程序踪迹文件可以通过标准库代码包 runtime/trace 和 net/http/pprof 中的程序来生成。

第 2 章:语法概览

  • 未使用的变量为了使其不报错可以在变量 x 的后面添加这样一行代码:_ = x
  • 类型断言表达式:v1.(I1) // v1 表示一个接口值,I1 表示一个接口类型
    • 如果 v1 是一个非接口值,那么必须在做类型断言之前把它转换成接口值。因为 Go 中的任何类型都是空接口类型的实现类型,所以一般会这样做:interface{}(v1).(I1)
    • 如果类型断言的结果为否,就意味着该类型断言是失败的。失败的类型断言会引发一个运行时恐慌(或称运行时异常),解决方法是:var i1, ok = interface{}(v1).(I1)。这里声明并赋值了两个变量,其中 ok 是布尔类型的变量,它的值体现了类型断言的成败。如果成功,i1 就会是经过类型转换后的 I1 类型的值,否则它将会是 I1 类型的零值(或称默认值)。
  • Go 编程有一个惯用法,即把 error 类型的结果作为函数结果列表的最后一员。
  • 类型 switch 语句将对类型进行判定,而不是值。类型 switch 语向的 switch 表达式会包含一个特殊的类型断言,例如 v.(type)。它虽然特殊,但是也要遵循类型断言的规则。其次,每个 case 表达式中包含的都是类型字面量而不是表达式。最后,fallthrough 语句不允许出现在类型 switch 语句中。Page 34。
  • range:若对数组、切片或字符串值进行迭代,且 := 左边只有一个迭代变量时,一定要小心。这时只会得到其中元素的索引,而不是元素本身;迭代为 nil 的通道值会让当前流程永远阻塞在 for 语句上。
  • defer:如果在延迟函数中使用外部变量,就应该通过参数传入。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func printNumbers() {
for i := 0; i < 5; i++{
defer func() {
fmt.Printf("%d", i)
}()
}
}

// 返回 55555

func printNumbers() {
for i := 0; i < 5; i++{
defer func(n int) {
fmt.Printf("%d", n)
}(i)
}
}

// 返回 43210
  • recover 函数一般和 defer 语句配合起来使用。recover 的返回值如果非空,则代表产生了运行时恐慌。
  • 可以把运行时恐慌的携带值转换为 error 类型值,并当作常规结果返回给调用方。这样既阻止了恐慌的扩散,又传递了引起恐慌的原因。
  • 检查运行时恐慌携带值的类型,并根据类型做不同的后续动作,这样可以精确地控制程序的错误处理行为。

第 3 章:并发编程综述