1
0
mirror of https://github.com/chai2010/advanced-go-programming-book.git synced 2025-05-24 04:22:22 +00:00

Merge branch 'master' of github.com:chai2010/advanced-go-programming-book

This commit is contained in:
Xargin 2018-05-28 12:18:14 +08:00
commit 9e104be09e
6 changed files with 10 additions and 10 deletions

View File

@ -389,7 +389,7 @@ func main() {
上面的程序中后台Goroutine向管道输入自然数序列main函数中输出序列。但是当break跳出for循环的时候后台Goroutine就处于无法被回收的状态了。 上面的程序中后台Goroutine向管道输入自然数序列main函数中输出序列。但是当break跳出for循环的时候后台Goroutine就处于无法被回收的状态了。
我们可以通过contxt包来避免做个问题: 我们可以通过context包来避免这个问题:
```go ```go

View File

@ -14,7 +14,7 @@ Go语言很多时候被描述为“类C语言”或者是“21世纪的C语
最后是基因图谱的右边一支这是对C语言的致敬。Go语言是对C语言最彻底的一次扬弃不仅仅是语法和C语言有着很多差异最重要的是舍弃了C语言中灵活但是危险的指针运算。而且Go语言还重新设计了C语言中部分不太合理运算符的优先级并在很多细微的地方都做了必要的打磨和改变。当然C语言中少即是多、简单直接的暴力编程哲学则被Go语言更彻底地发扬光大了Go语言居然只有25个关键字sepc语言规范还不到50页))。 最后是基因图谱的右边一支这是对C语言的致敬。Go语言是对C语言最彻底的一次扬弃不仅仅是语法和C语言有着很多差异最重要的是舍弃了C语言中灵活但是危险的指针运算。而且Go语言还重新设计了C语言中部分不太合理运算符的优先级并在很多细微的地方都做了必要的打磨和改变。当然C语言中少即是多、简单直接的暴力编程哲学则被Go语言更彻底地发扬光大了Go语言居然只有25个关键字sepc语言规范还不到50页))。
Go语言其它的一些特性零散地来自于其他一些编程语言比如iota语法是从APL语言借鉴词法作用域与嵌套函数等特性来自于Scheme语言和其他很多编程语言。Go语言中也有很多自己发明创新的设计。比如Go语言的切片为轻量级动态数组提供了有效的随机存取的性能这可能会让人联想到链表的底层的共享机制。还有Go语言新发明的defer语句Ken发明也是神来之笔。 Go语言其它的一些特性零散地来自于其他一些编程语言比如iota语法是从APL语言借鉴词法作用域与嵌套函数等特性来自于Scheme语言和其他很多编程语言。Go语言中也有很多自己发明创新的设计。比如Go语言的切片为轻量级动态数组提供了有效的随机存取的性能这可能会让人联想到链表的底层的共享机制。还有Go语言新发明的defer语句Ken发明也是神来之笔。
## 来自贝尔实验室特有基因 ## 来自贝尔实验室特有基因

View File

@ -106,7 +106,7 @@ func main() {
func main() { func main() {
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
// 通过函数函数传入i // 通过函数传入i
// defer 语句会马上对调用参数求值 // defer 语句会马上对调用参数求值
defer func(i int){ println(i) } (i) defer func(i int){ println(i) } (i)
} }

View File

@ -2,7 +2,7 @@
Go语言最吸引人的地方是它内建的并发支持。Go语言并发体系的理论是C.A.R Hoare在1978年提出的CSPCommunicating Sequential Process通讯顺序进程。CSP有着精确的数学模型并实际应用在了Hoare实际参与设计的T9000通用计算机上。从NewSqueak、Alef、Limbo到现在的Go语言对于对CSP有着20多年实战经验的Rob Pike来说他更关注的是将CSP应用在通用编程语言上的潜力。作为Go并发编程核心的CSP理论的核心概念只有一个同步通信。关于同步通信的话题我们在前面一节已经讲过本节我们将简单介绍下Go语言中常见的并发模式。 Go语言最吸引人的地方是它内建的并发支持。Go语言并发体系的理论是C.A.R Hoare在1978年提出的CSPCommunicating Sequential Process通讯顺序进程。CSP有着精确的数学模型并实际应用在了Hoare实际参与设计的T9000通用计算机上。从NewSqueak、Alef、Limbo到现在的Go语言对于对CSP有着20多年实战经验的Rob Pike来说他更关注的是将CSP应用在通用编程语言上的潜力。作为Go并发编程核心的CSP理论的核心概念只有一个同步通信。关于同步通信的话题我们在前面一节已经讲过本节我们将简单介绍下Go语言中常见的并发模式。
首先要明确一个概念并发不是并行。并发更关注的是程序的设计层面并发的程序完全是可以顺序执行的只有在真正的多核CPU上才可能真正地同时运行。并行更关注的是程序的运行层面并行一般是简单的大量重复例如GPU中对图像处理都会有大量的并行运算。Go语言从一开始设计就围绕着如何能在编程语言的层级为更好的编写并发程序设计一个简洁安全高效的抽象模型让程序员专注于分解问题和组合方案而且不用被线程管理和信号互斥这些繁琐的操作分散精力。 首先要明确一个概念并发不是并行。并发更关注的是程序的设计层面并发的程序完全是可以顺序执行的只有在真正的多核CPU上才可能真正地同时运行。并行更关注的是程序的运行层面并行一般是简单的大量重复例如GPU中对图像处理都会有大量的并行运算。Go语言从一开始设计就围绕着如何能在编程语言的层级为更好的编写并发程序设计一个简洁安全高效的抽象模型让程序员专注于分解问题和组合方案而且不用被线程管理和信号互斥这些繁琐的操作分散精力。
在并发编程中对共享资源的正确访问需要精确的控制在目前的绝大多数语言中都是通过加锁等线程同步方案来解决这一困难问题而Go语言却另辟蹊径它将共享的值通过信道传递(实际上多个独立执行的线程很少主动共享资源)。在任意给定的时刻最好只有一个Goroutine能够拥有该资源。数据竞争从设计层面上就被杜绝了。为了提倡这种思考方式Go语言将其并发编程哲学化为一句口号 在并发编程中对共享资源的正确访问需要精确的控制在目前的绝大多数语言中都是通过加锁等线程同步方案来解决这一困难问题而Go语言却另辟蹊径它将共享的值通过信道传递(实际上多个独立执行的线程很少主动共享资源)。在任意给定的时刻最好只有一个Goroutine能够拥有该资源。数据竞争从设计层面上就被杜绝了。为了提倡这种思考方式Go语言将其并发编程哲学化为一句口号
@ -651,7 +651,7 @@ func main() {
对于生产者、消费者并发模型我们当然可以通过降低生产者的产能来避免资源的浪费。但在很多场景中生产者才是核心对象它们生产出各种问题或任务单据这时候产出的问题是必须要解决的、任务单据也是必须要完成的。在现实生活中制造各种生活垃圾的海量人类其实就是垃圾生产者而清理生活垃圾的少量的清洁工就是垃圾消费者。在网络服务中提交POST数据的海量用户则变成了生产者Web后台服务则对应POST数据的消费者。海量生产者的问题也就变成了如何构造一个能够处理海量请求的Web服务假设每分钟百万级请求 对于生产者、消费者并发模型我们当然可以通过降低生产者的产能来避免资源的浪费。但在很多场景中生产者才是核心对象它们生产出各种问题或任务单据这时候产出的问题是必须要解决的、任务单据也是必须要完成的。在现实生活中制造各种生活垃圾的海量人类其实就是垃圾生产者而清理生活垃圾的少量的清洁工就是垃圾消费者。在网络服务中提交POST数据的海量用户则变成了生产者Web后台服务则对应POST数据的消费者。海量生产者的问题也就变成了如何构造一个能够处理海量请求的Web服务假设每分钟百万级请求
在Web服务中用户提交的每个POST请求可以看作是一个Job任务而服务器是通过后台的Worker工作者来消费这些Job任务。当面向海量的Job处理时我们一般可以通过构造一个Worker工作者池来提高Job的处理效率通过通过一个带缓存的Job管道来接收新的任务请求避免任务请求功能无法响应Job请求接收管道和Worker工作者池通过分发系统来衔接。 在Web服务中用户提交的每个POST请求可以看作是一个Job任务而服务器是通过后台的Worker工作者来消费这些Job任务。当面向海量的Job处理时我们一般可以通过构造一个Worker工作者池来提高Job的处理效率通过一个带缓存的Job管道来接收新的任务请求避免任务请求功能无法响应Job请求接收管道和Worker工作者池通过分发系统来衔接。
我们可以用管道来模拟工作者池:当需要处理一个任务时,先从工作者池取一个工作者,处理完任务之后将工作者返回给工作者池。`WorkerPool`对应工作者池,`Worker`对应工作者。 我们可以用管道来模拟工作者池:当需要处理一个任务时,先从工作者池取一个工作者,处理完任务之后将工作者返回给工作者池。`WorkerPool`对应工作者池,`Worker`对应工作者。
@ -889,7 +889,7 @@ func main() {
当并发体超时或`main`主动停止工作者Goroutine时每个工作者都可以安全退出。 当并发体超时或`main`主动停止工作者Goroutine时每个工作者都可以安全退出。
Go语言是带内存自动回收的特性因此内存一般不会泄漏。在前面素数筛的例子中`GenerateNatural``PrimeFilter`函数内部都启动了新的Goroutine`main`函数不再使用管道时后台Goroutine有泄漏的风险。我们可以通过`contxt`包来避免做个问题,下面是改进的素数筛实现: Go语言是带内存自动回收的特性因此内存一般不会泄漏。在前面素数筛的例子中`GenerateNatural``PrimeFilter`函数内部都启动了新的Goroutine`main`函数不再使用管道时后台Goroutine有泄漏的风险。我们可以通过`context`包来避免这个问题,下面是改进的素数筛实现:
```go ```go
// 返回生成自然数序列的管道: 2, 3, 4, ... // 返回生成自然数序列的管道: 2, 3, 4, ...

View File

@ -103,7 +103,7 @@ func ParseJSON(input string) (s *Syntax, err error) {
} }
``` ```
在标准库中的`json`在内部递归解析JSON数据的时候如果遇到错误会通过抛出异常的方式来快速跳出深度嵌套的函数调用然后由最外以及的接口通过`recover`捕获`panic`,然后返回相应的错误信息。 在标准库中的`json`在内部递归解析JSON数据的时候如果遇到错误会通过抛出异常的方式来快速跳出深度嵌套的函数调用然后由最外一级的接口通过`recover`捕获`panic`,然后返回相应的错误信息。
Go语言库的实现习惯: 即使在包内部使用了`panic`,但是在导出函数时会被转化为明确的错误值。 Go语言库的实现习惯: 即使在包内部使用了`panic`,但是在导出函数时会被转化为明确的错误值。
@ -141,7 +141,7 @@ type CallerInfo struct {
} }
``` ```
其中`Error`为接口类型,是`error`接口类型的扩展,用于给错误增加调用栈信息,同时支持错误的多级嵌套包装,支持支持错误码格式。为了使用方便,我们可以定义以下的辅助函数: 其中`Error`为接口类型,是`error`接口类型的扩展,用于给错误增加调用栈信息,同时支持错误的多级嵌套包装,支持错误码格式。为了使用方便,我们可以定义以下的辅助函数:
```go ```go
func New(msg string) error func New(msg string) error

View File

@ -126,7 +126,7 @@ go.string."gopher" SRODATA dupok size=6
rel 0+8 t=1 go.string."gopher"+0 rel 0+8 t=1 go.string."gopher"+0
``` ```
输出中出现了一个新的符号go.string."gopher",根据其长度和内容分析可以猜测是对应底层的"gopher"字符串数据。因为Go语言的字符串并不是值类型Go字符串只是一只读的引用类型。假设多个代码中出现了相同的"gopher"字符串时程序链接后其实都是引用的同一个符号go.string."gopher"。因此该符号有一个SRODATA标志表示这个数据在只读内存段dupok表示出现多个相同符号时只保留一个就可以了。 输出中出现了一个新的符号go.string."gopher",根据其长度和内容分析可以猜测是对应底层的"gopher"字符串数据。因为Go语言的字符串并不是值类型Go字符串只是一只读的引用类型。假设多个代码中出现了相同的"gopher"字符串时程序链接后其实都是引用的同一个符号go.string."gopher"。因此该符号有一个SRODATA标志表示这个数据在只读内存段dupok表示出现多个相同符号时只保留一个就可以了。
而真正的Go字符串变量Name对应的大小却只有16个字节了。其实Name变量并没有直接对应“gopher”字符串而是对应reflect.StringHeader结构体 而真正的Go字符串变量Name对应的大小却只有16个字节了。其实Name变量并没有直接对应“gopher”字符串而是对应reflect.StringHeader结构体
@ -252,7 +252,7 @@ Go语言函数在函数调用时完全通过栈传递调用参数和返回值
## 特殊字符 ## 特殊字符
Go语言函数或方法符号在编译为目标文件后目标文件中的每个符号均包含对应包的觉得导入路径。因此目标文件的符号可能非常复杂比如“path/to/pkg.(*SomeType).SomeMethod”或“go.string."abc"”。目标文件的符号名中不仅仅包含普通的字母还可能包含诸多特殊字符。而Go语言的汇编器是从plan9移植过来的二把刀并不能处理这些特殊的字符导致了用Go汇编语言手工实现Go诸多特性时遇到种种限制。 Go语言函数或方法符号在编译为目标文件后目标文件中的每个符号均包含对应包的绝对导入路径。因此目标文件的符号可能非常复杂比如“path/to/pkg.(*SomeType).SomeMethod”或“go.string."abc"”。目标文件的符号名中不仅仅包含普通的字母还可能包含诸多特殊字符。而Go语言的汇编器是从plan9移植过来的二把刀并不能处理这些特殊的字符导致了用Go汇编语言手工实现Go诸多特性时遇到种种限制。
Go汇编语言同样遵循Go语言少即是多的哲学它只保留了最基本的特性定义变量和全局函数。同时为了简化Go汇编器的词法扫描程序的实现特别引入了Unicode中的中点`·`和大写的除法`/`对应的Unicode码点为`U+00B7``U+2215`。汇编器编译后,中点`·`会被替换为ASCII中的点“.”大写点除法会被替换为ASCII码中的除法“/”,比如`math/rand·Int`会被替换为`math/rand.Int`。这样可以将点和浮点数中的小数点、大写的除法和表达式中的除法符号分开,可以简化汇编程序此法分析部分的实现。 Go汇编语言同样遵循Go语言少即是多的哲学它只保留了最基本的特性定义变量和全局函数。同时为了简化Go汇编器的词法扫描程序的实现特别引入了Unicode中的中点`·`和大写的除法`/`对应的Unicode码点为`U+00B7``U+2215`。汇编器编译后,中点`·`会被替换为ASCII中的点“.”大写点除法会被替换为ASCII码中的除法“/”,比如`math/rand·Int`会被替换为`math/rand.Int`。这样可以将点和浮点数中的小数点、大写的除法和表达式中的除法符号分开,可以简化汇编程序此法分析部分的实现。