├── .github └── ISSUE_TEMPLATE │ └── bug-report.md ├── .gitignore ├── FOREWORD.md ├── README.md ├── STYLE.md ├── SUMMARY.md ├── cover.png ├── go.mod ├── go.sum └── source └── gotest ├── benchmark.go ├── benchmark_test.go ├── example.go ├── example_test.go ├── maintest_test.go ├── subbenchmark_test.go ├── subparallel_test.go ├── subunit_test.go ├── unit.go └── unit_test.go /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 图书错误、疑问反馈 3 | about: 反馈图书中的错误,任何错误、甚至是疑问都欢迎。 4 | title: "[勘误]反馈一个第M章第N节的错别字" 5 | labels: bug 6 | assignees: RainbowMango 7 | 8 | --- 9 | 10 | **问题描述** 11 | A clear and concise description of what the bug is. 12 | 13 | **如何找到这个错误** 14 | - 章节: 15 | - 页码: 16 | 17 | **您认为应该如何?** 18 | 19 | **图片** 20 | 如果有可能,尽量提供图片。 21 | 22 | **其他补充信息** 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Output of IDEs 15 | .idea 16 | 17 | # Output of build 18 | pkg 19 | 20 | # OSX trash 21 | .DS_Store -------------------------------------------------------------------------------- /FOREWORD.md: -------------------------------------------------------------------------------- 1 | 2 | # 项目地址(欢迎勘误或投稿) 3 | [GitHub](https://github.com/RainbowMango/GoExpertProgramming) 4 | 5 | # 联系作者 6 | [邮件](mailto:qdurenhongcai@gmail.com) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 《GO专家编程》 2 | 3 | ![](cover.png) 4 | 5 | ## 图书介绍 6 | 本书涵盖内容: 7 | - 常见数据结构、控制结构的实现原理剖析; 8 | - 常用标准库实现原理剖析; 9 | - 工具链使用、原理剖析(如 Go test、Go Module等); 10 | - 编程陷阱及相关真实案例解析; 11 | 12 | ## 纸质版 13 | - 当当网链接:http://product.dangdang.com/28986429.html 14 | - 京东商城链接:https://item.jd.com/12920392.html 15 | 16 | ## 致谢 17 | 本书写作过程中,得到了广大读者的支持和鼓励,作者本人也很高兴本书能够帮助到读者。 18 | 19 | #### 读者鼓励: 20 | - @Wang-Kai: 21 | > 这一定是我在 Github 发现的最有价值的仓库之一。 22 | 23 | - @liyonglion: 24 | > 感谢贡献如此好文,结合你的文章,再看源码如虎添翼。 25 | 26 | - @li-keli: 27 | > 首先,确实如你所说 是让初级进阶的文档 28 | 下午github摸鱼看到这个库,草草阅读后,发现挖掘到了宝藏。 29 | 忍不住收藏细细的再多读几遍。 30 | 我读过基本Go的书,比如《Go实战》《Go并发编程》,对比之下,此文档也是干货满满,从目录看,直击我这种3年以内经验人的痛点,只会用,只会写项目,但是不理解底层,不理解实现,写不出高性能项目,可能遇到问题都不知道如何快速排查。 31 | 其实我目前这种层次的人不是不知道问题所在,只是手头的资料并没有系统化,都很碎。 32 | 市面上的go中文书籍或者文档都比较基础,官方文档一方面是英文,有些阅读障碍,另一方面,有部分确实比较难懂。 33 | 34 | #### 热心读者 35 | 部分热心读者提交了大最PR帮助作者修复书中错误,在此向这些读者表示感谢: 36 | - @marjune163 37 | - @twz915 38 | - @xiaoxuanzi 39 | - @weiyuanke 40 | - @wangdayong228 41 | - @notech 42 | - @li-keli 43 | - @hjlarry 44 | - @exqlnet 45 | - Bingao-hn 46 | 47 | 特别感谢@marjune163帮忙修复了大量错误,本书出版后,特向@marjune163寄送了一本纸质书,聊表谢意。 48 | 49 | ## 后续 50 | 鉴于本仓库内容与纸质图书已有相当大的差异,也考虑到出版社的利益,暂时下架部分章节,待与出版社沟通后再陆续放出。 51 | -------------------------------------------------------------------------------- /STYLE.md: -------------------------------------------------------------------------------- 1 | 2 | # 源文件组织形式 3 | ## 章节目录 4 | 每个章节文章及配图放到一个目录,目录名称格式为"chapterXX",其中XX表示章节序号,不足两位以0补齐。 5 | 6 | 例如,第一章目录名为chapter01。 7 | 8 | ## 源码文件 9 | 每个章节都对应一个源码文件,文件命名格式为"[节]-[源码文件名].md"。 10 | 11 | 例如,某章第一节的源码文件名为1.1-go_schedule.md。 12 | 13 | 注:源码文件名中不再体现`章`,以避免章节调整时再重命名。 14 | 15 | ## 配图 16 | 每章的配图放到章目录下的images子目录中。每节的配图统一命名,格式为"[本节关键词]-[本节图片序号]-[图片名]"。 17 | 18 | 例如,第一节第一张图命令为:chan-01-chan_queue.png 19 | 20 | 建议同时保留图片原型,以便修改方便。图片原型文件命令与图片一致,只是后缀不同。 21 | 22 | # 画图工具 23 | 本书中所有原创图片都使用[`ProcessOn`](https://www.processon.com/i/5ba9b549e4b075b9fe553d20)在线工具完成。 24 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | * [前言](FOREWORD.md) 3 | * [第一章:常见数据结构实现原理](chapter01/README.md) 4 | * [1.1 chan](chapter01/1.1-chan.md) 5 | * [1.2 slice](chapter01/1.2-slice.md) 6 | * [1.3 map](chapter01/1.3-map.md) 7 | * [1.4 struct](chapter01/1.4-struct.md) 8 | * [1.5 iota](chapter01/1.5-iota.md) 9 | * [1.6 string](chapter01/1.6-string.md) 10 | * [第二章:常见控制结构实现原理](chapter02/README.md) 11 | * [2.1 defer](chapter02/2.1-defer.md) 12 | * [2.1.1 defer 陷阱](chapter02/2.1.1-defer-recover.md) 13 | * [2.2 select](chapter02/2.2-select.md) 14 | * [2.3 range](chapter02/2.3-range.md) 15 | * [2.4 mutex](chapter02/2.4-mutex.md) 16 | * [2.5 rwmutex](chapter02/2.5-rwmutex.md) 17 | * [第三章:协程](chapter03/README.md) 18 | * [3.1 协程调度](chapter03/3.1-go_schedule.md) 19 | * [第四章:内存管理](chapter04/README.md) 20 | * [4.1 内存分配原理](chapter04/4.1-memory_alloc.md) 21 | * [4.2 垃圾回收原理](chapter04/4.2-garbage_collection.md) 22 | * [4.3 逃逸分析](chapter04/4.3-escape_analysis.md) 23 | * [第五章:并发控制](chapter05/README.md) 24 | * [5.1 Channel](chapter05/5.1-channel.md) 25 | * [5.2 WaitGroup](chapter05/5.2-waitgroup.md) 26 | * [5.3 Context](chapter05/5.3-context.md) 27 | * [第六章:反射](chapter06/README.md) 28 | * [6.1 反射机制](chapter06/6.1-reflect.md) 29 | * [第七章:测试](chapter07/README.md) 30 | * [7.1 快速开始](chapter07/7.1-foreword.md) 31 | * [7.1.1 单元测试](chapter07/7.1.1-unit_test.md) 32 | * [7.1.2 性能测试](chapter07/7.1.2-benchmark_test.md) 33 | * [7.1.3 示例测试](chapter07/7.1.3-example_test.md) 34 | * [7.2 进阶测试](chapter07/7.2-foreword.md) 35 | * [7.2.1 子测试](chapter07/7.2.1-sub_test.md) 36 | * [7.2.2 Main测试](chapter07/7.2.2-main_test.md) 37 | * [7.3 实现原理](chapter07/7.3-foreword.md) 38 | * [7.3.1 testing.common公共类](chapter07/7.3.1-common.md) 39 | * [7.3.2 testing.TB接口](chapter07/7.3.2-tb_interface.md) 40 | * [7.3.3 单元测试实现原理](chapter07/7.3.3-unit.md) 41 | * [7.3.4 性能测试实现原理](chapter07/7.3.4-benchmark.md) 42 | * [7.3.5 示例测试实现原理](chapter07/7.3.5-example.md) 43 | * [7.3.6 Main测试实现原理](chapter07/7.3.6-main.md) 44 | * [7.3.7 go test工作机制](chapter07/7.3.7-go_test.md) 45 | * [7.4 扩展阅读](chapter07/7.4-foreword.md) 46 | * [7.4.1 测试参数](chapter07/7.4.1-parameter.md) 47 | * [7.4.2 基准测试分析](chapter07/7.4.2-benchstat.md) 48 | * [第八章:httptest](chapter08/README.md) 49 | * [第九章:定时器](chapter09/README.md) 50 | * [9.1 Timer](chapter09/9.1-foreword.md) 51 | * [9.1.1 快速开始](chapter09/9.1.1-timer_quick_start.md) 52 | * [9.1.2 实现原理](chapter09/9.1.2-timer_principle.md) 53 | * [9.2 Ticker](chapter09/9.2-foreword.md) 54 | * [9.2.1 快速开始](chapter09/9.2.1-ticker_quick_start.md) 55 | * [9.2.2 实现原理](chapter09/9.2.2-ticker_principle.md) 56 | * [9.3 timer](chapter09/9.3-foreword.md) 57 | * [9.3.1 实现原理](chapter09/9.3.1-timersproc_principle.md) 58 | * [9.4 案例](chapter09/9.4-foreword.md) 59 | * [9.4.1 开源库资源泄露](chapter09/9.4.1-ticker_leak_case.md) 60 | * [第十章:语法糖](chapter10/README.md) 61 | * [10.1 简短变量声明](chapter10/1-foreword.md) 62 | * [10.1.1 热身测验](chapter10/1.1-warmup.md) 63 | * [10.1.2 使用规则](chapter10/1.2-rules.md) 64 | * [10.2 可变参函数](chapter10/2-variadic.md) 65 | * [第十一章:GO语言版本管理](chapter11/README.md) 66 | * [11.1 GO语言安装](chapter11/1.1-install.md) 67 | * [11.2 GO语言卸载](chapter11/1.2-uninstall.md) 68 | * [第十二章:GO依赖管理](chapter12/README.md) 69 | * [12.1 GOPATH](chapter12/1-gopath.md) 70 | * [12.2 vendor](chapter12/2-vendor.md) 71 | * [12.3 module](chapter12/3-foreword.md) 72 | * [12.3.1 基础概念](chapter12/3.1-basic.md) 73 | * [12.3.2 快速实践](chapter12/3.2-quickstart.md) 74 | * [12.3.3 replace指令](chapter12/3.3-module-replace.md) 75 | * [12.3.4 exclude指令](chapter12/3.4-module-exclude.md) 76 | * [12.3.5 indirect含义](chapter12/3.5-module-indirect.md) 77 | * [12.3.6 版本选择机制](chapter12/3.6-module-version.md) 78 | * [12.3.7 incompatible](chapter12/3.7-module-incompatible.md) 79 | * [12.3.8 伪版本](chapter12/3.8-module-pseudo-version.md) 80 | * [12.3.9 依赖包存储](chapter12/3.9-module-storage.md) 81 | * [12.3.10 go.sum文件](chapter12/3.10-module-go-sum.md) 82 | * [附录一:编程陷阱](appendix/README.md) 83 | * [1 切片追加](appendix/append.md) 84 | * [2 循环变量绑定](appendix/unpinned.md) 85 | -------------------------------------------------------------------------------- /cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RainbowMango/GoExpertProgramming/9ae21700da666bedb3bdf714c2558d13e663bbf8/cover.png -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/rainbowmango/goexpertprogramming 2 | 3 | go 1.15 4 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RainbowMango/GoExpertProgramming/9ae21700da666bedb3bdf714c2558d13e663bbf8/go.sum -------------------------------------------------------------------------------- /source/gotest/benchmark.go: -------------------------------------------------------------------------------- 1 | package gotest 2 | 3 | // MakeSliceWithPreAlloc 不预分配 4 | func MakeSliceWithoutAlloc() []int { 5 | var newSlice []int 6 | 7 | for i := 0; i < 100000; i++ { 8 | newSlice = append(newSlice, i) 9 | } 10 | 11 | return newSlice 12 | } 13 | 14 | // MakeSliceWithPreAlloc 通过预分配Slice的存储空间构造 15 | func MakeSliceWithPreAlloc() []int { 16 | var newSlice []int 17 | 18 | newSlice = make([]int, 0, 100000) 19 | for i := 0; i < 100000; i++ { 20 | newSlice = append(newSlice, i) 21 | } 22 | 23 | return newSlice 24 | } 25 | -------------------------------------------------------------------------------- /source/gotest/benchmark_test.go: -------------------------------------------------------------------------------- 1 | package gotest 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func BenchmarkMakeSliceWithoutAlloc(b *testing.B) { 9 | for i := 0; i < b.N; i++ { 10 | MakeSliceWithoutAlloc() 11 | } 12 | } 13 | 14 | func BenchmarkMakeSliceWithPreAlloc(b *testing.B) { 15 | for i := 0; i < b.N; i++ { 16 | MakeSliceWithPreAlloc() 17 | } 18 | } 19 | 20 | func BenchmarkSetBytes(b *testing.B) { 21 | b.SetBytes(1024 * 1024) 22 | for i := 0; i < b.N; i++ { 23 | time.Sleep(1 * time.Second) // 模拟待测函数 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /source/gotest/example.go: -------------------------------------------------------------------------------- 1 | package gotest 2 | 3 | import "fmt" 4 | 5 | // SayHello 打印一行字符串 6 | func SayHello() { 7 | fmt.Println("Hello World") 8 | } 9 | 10 | // SayGoodbye 打印两行字符串 11 | func SayGoodbye() { 12 | fmt.Println("Hello,") 13 | fmt.Println("goodbye") 14 | } 15 | 16 | // PrintNames 打印学生姓名 17 | func PrintNames() { 18 | students := make(map[int]string, 4) 19 | students[1] = "Jim" 20 | students[2] = "Bob" 21 | students[3] = "Tom" 22 | students[4] = "Sue" 23 | for _, value := range students { 24 | fmt.Println(value) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /source/gotest/example_test.go: -------------------------------------------------------------------------------- 1 | package gotest_test 2 | 3 | import "github.com/rainbowmango/goexpertprogramming/source/gotest" 4 | 5 | // 检测单行输出 6 | func ExampleSayHello() { 7 | gotest.SayHello() 8 | // OutPut: Hello World 9 | } 10 | 11 | // 检测多行输出 12 | func ExampleSayGoodbye() { 13 | gotest.SayGoodbye() 14 | // OutPut: 15 | // Hello, 16 | // goodbye 17 | } 18 | 19 | // 检测乱序输出 20 | func ExamplePrintNames() { 21 | gotest.PrintNames() 22 | // Unordered output: 23 | // Jim 24 | // Bob 25 | // Tom 26 | // Sue 27 | } 28 | -------------------------------------------------------------------------------- /source/gotest/maintest_test.go: -------------------------------------------------------------------------------- 1 | package gotest_test 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | ) 7 | 8 | // TestMain 用于主动执行各种测试,可以测试前后做setup和tear-down操作 9 | func TestMain(m *testing.M) { 10 | println("TestMain setup.") 11 | 12 | retCode := m.Run() // 执行测试,包括单元测试、性能测试和示例测试 13 | 14 | println("TestMain tear-down.") 15 | os.Exit(retCode) 16 | } 17 | -------------------------------------------------------------------------------- /source/gotest/subbenchmark_test.go: -------------------------------------------------------------------------------- 1 | package gotest_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/rainbowmango/goexpertprogramming/source/gotest" 7 | ) 8 | 9 | func benchSub1(b *testing.B) { 10 | for i := 0; i < b.N; i++ { 11 | gotest.MakeSliceWithoutAlloc() 12 | } 13 | } 14 | 15 | func benchSub2(b *testing.B) { 16 | for i := 0; i < b.N; i++ { 17 | gotest.MakeSliceWithoutAlloc() 18 | } 19 | } 20 | 21 | func benchSub3(b *testing.B) { 22 | for i := 0; i < b.N; i++ { 23 | gotest.MakeSliceWithoutAlloc() 24 | } 25 | } 26 | 27 | func BenchmarkSub(b *testing.B) { 28 | b.Run("A=1", benchSub1) 29 | b.Run("A=2", benchSub2) 30 | b.Run("B=1", benchSub3) 31 | } 32 | -------------------------------------------------------------------------------- /source/gotest/subparallel_test.go: -------------------------------------------------------------------------------- 1 | package gotest_test 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | // 并发子测试,无实际测试工作,仅用于演示 9 | func parallelTest1(t *testing.T) { 10 | t.Parallel() 11 | time.Sleep(3 * time.Second) 12 | // do some testing 13 | } 14 | 15 | // 并发子测试,无实际测试工作,仅用于演示 16 | func parallelTest2(t *testing.T) { 17 | t.Parallel() 18 | time.Sleep(2 * time.Second) 19 | // do some testing 20 | } 21 | 22 | // 并发子测试,无实际测试工作,仅用于演示 23 | func parallelTest3(t *testing.T) { 24 | t.Parallel() 25 | time.Sleep(1 * time.Second) 26 | // do some testing 27 | } 28 | 29 | // TestSubParallel 通过把多个子测试放到一个组中并发执行,同时多个子测试可以共享setup和tear-down 30 | func TestSubParallel(t *testing.T) { 31 | // setup 32 | t.Logf("Setup") 33 | 34 | t.Run("group", func(t *testing.T) { 35 | t.Run("Test1", parallelTest1) 36 | t.Run("Test2", parallelTest2) 37 | t.Run("Test3", parallelTest3) 38 | }) 39 | 40 | // tear down 41 | t.Logf("teardown") 42 | } 43 | -------------------------------------------------------------------------------- /source/gotest/subunit_test.go: -------------------------------------------------------------------------------- 1 | package gotest_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/rainbowmango/goexpertprogramming/source/gotest" 7 | ) 8 | 9 | // sub1 为子测试,只做加法测试 10 | func sub1(t *testing.T) { 11 | var a = 1 12 | var b = 2 13 | var expected = 3 14 | 15 | actual := gotest.Add(a, b) 16 | if actual != expected { 17 | t.Errorf("Add(%d, %d) = %d; expected: %d", a, b, actual, expected) 18 | } 19 | } 20 | 21 | // sub2 为子测试,只做加法测试 22 | func sub2(t *testing.T) { 23 | var a = 1 24 | var b = 2 25 | var expected = 3 26 | 27 | actual := gotest.Add(a, b) 28 | if actual != expected { 29 | t.Errorf("Add(%d, %d) = %d; expected: %d", a, b, actual, expected) 30 | } 31 | } 32 | 33 | // sub3 为子测试,只做加法测试 34 | func sub3(t *testing.T) { 35 | var a = 1 36 | var b = 2 37 | var expected = 3 38 | 39 | actual := gotest.Add(a, b) 40 | if actual != expected { 41 | t.Errorf("Add(%d, %d) = %d; expected: %d", a, b, actual, expected) 42 | } 43 | } 44 | 45 | // TestSub 内部调用sub1、sub2和sub3三个子测试 46 | func TestSub(t *testing.T) { 47 | // setup code 48 | 49 | t.Run("A=1", sub1) 50 | t.Run("A=2", sub2) 51 | t.Run("B=1", sub3) 52 | 53 | // tear-down code 54 | } 55 | -------------------------------------------------------------------------------- /source/gotest/unit.go: -------------------------------------------------------------------------------- 1 | package gotest 2 | 3 | // Add 方法用于演示go test使用 4 | func Add(a int, b int) int { 5 | return a + b 6 | } 7 | -------------------------------------------------------------------------------- /source/gotest/unit_test.go: -------------------------------------------------------------------------------- 1 | package gotest_test 2 | 3 | import ( 4 | "flag" 5 | "testing" 6 | 7 | "github.com/rainbowmango/goexpertprogramming/source/gotest" 8 | ) 9 | 10 | func TestAdd(t *testing.T) { 11 | var a = 1 12 | var b = 2 13 | var expected = 3 14 | 15 | actual := gotest.Add(a, b) 16 | if actual != expected { 17 | t.Errorf("Add(%d, %d) = %d; expected: %d", a, b, actual, expected) 18 | } 19 | } 20 | 21 | // TestArgs 用于演示如何解析-args参数 22 | func TestArgs(t *testing.T) { 23 | if !flag.Parsed() { 24 | flag.Parse() 25 | } 26 | 27 | argList := flag.Args() // flag.Args() 返回 -args 后面的所有参数,以切片表示,每个元素代表一个参数 28 | for _, arg := range argList { 29 | if arg == "cloud" { 30 | t.Log("Running in cloud.") 31 | } else { 32 | t.Log("Running in other mode.") 33 | } 34 | } 35 | } 36 | --------------------------------------------------------------------------------