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

fix typos

This commit is contained in:
sfw 2018-08-09 19:45:49 +08:00
parent 47ac7a8d78
commit 0804a08d3a
2 changed files with 7 additions and 7 deletions

View File

@ -308,7 +308,7 @@ func main() {
可保证打印出“hello, world”。该程序首先对`msg`进行写入,然后在`done`管道上发送同步信号,随后从`done`接收对应的同步信号,最后执行`println`函数。 可保证打印出“hello, world”。该程序首先对`msg`进行写入,然后在`done`管道上发送同步信号,随后从`done`接收对应的同步信号,最后执行`println`函数。
若在关闭信道后继续从中接收数据,接收者就会收到该信道返回的零值。因此在这个例子中,用`close(c)`关闭管道代替`done <- false`依然能保证该程序产生相同的行为。 若在关闭Channel后继续从中接收数据接收者就会收到该Channel返回的零值。因此在这个例子中,用`close(c)`关闭管道代替`done <- false`依然能保证该程序产生相同的行为。
```go ```go
var done = make(chan bool) var done = make(chan bool)
@ -326,7 +326,7 @@ func main() {
} }
``` ```
**对于从无缓冲信道进行的接收,发生在对该信道进行的发送完成之前。** **对于从无缓冲Channel进行的接收发生在对该Channel进行的发送完成之前。**
基于上面这个规则可知交换两个Goroutine中的接收和发送操作也是可以的但是很危险 基于上面这个规则可知交换两个Goroutine中的接收和发送操作也是可以的但是很危险
@ -345,9 +345,9 @@ func main() {
} }
``` ```
也可保证打印出“hello, world”。因为`main`线程中`done <- true`发送完成前,后台线程`<-done`接收已经开始,这保证`msg = "hello, world"`被执行了,所以之后`println(msg)`的msg已经被赋值过了。简而言之后台线程首先对`msg`进行写入,然后从`done`中接收信号,随后`main`线程向`done`发送对应的信号,最后执行`println`函数完成。但是,若该信道为带缓冲的(例如,`done = make(chan bool, 1)``main`线程的`done <- true`接收操作将不会被后台线程的`<-done`接收操作阻塞该程序将无法保证打印出“hello, world”。 也可保证打印出“hello, world”。因为`main`线程中`done <- true`发送完成前,后台线程`<-done`接收已经开始,这保证`msg = "hello, world"`被执行了,所以之后`println(msg)`的msg已经被赋值过了。简而言之后台线程首先对`msg`进行写入,然后从`done`中接收信号,随后`main`线程向`done`发送对应的信号,最后执行`println`函数完成。但是,若该Channel为带缓冲的(例如,`done = make(chan bool, 1)``main`线程的`done <- true`接收操作将不会被后台线程的`<-done`接收操作阻塞该程序将无法保证打印出“hello, world”。
对于带缓冲的Channel**对于Channel的第`K`个接收完成操作发生在第`K+C`个发送操作完成之前,其中`C`是Channel的缓存大小。** 如果将`C`设置为0自然就对应无缓存的Channel也即使第K个接收完成在第K个发送完成之前。因为无缓存的Channel只能同步发1个也就简化为前面无缓存Channel的规则**对于从无缓冲信道进行的接收,发生在对该信道进行的发送完成之前。** 对于带缓冲的Channel**对于Channel的第`K`个接收完成操作发生在第`K+C`个发送操作完成之前,其中`C`是Channel的缓存大小。** 如果将`C`设置为0自然就对应无缓存的Channel也即使第K个接收完成在第K个发送完成之前。因为无缓存的Channel只能同步发1个也就简化为前面无缓存Channel的规则**对于从无缓冲Channel进行的接收发生在对该Channel进行的发送完成之前。**
我们可以根据控制Channel的缓存大小来控制并发执行的Goroutine的最大数目, 例如: 我们可以根据控制Channel的缓存大小来控制并发执行的Goroutine的最大数目, 例如:

View File

@ -4,13 +4,13 @@ Go语言最吸引人的地方是它内建的并发支持。Go语言并发体系
首先要明确一个概念并发不是并行。并发更关注的是程序的设计层面并发的程序完全是可以顺序执行的只有在真正的多核CPU上才可能真正地同时运行。并行更关注的是程序的运行层面并行一般是简单的大量重复例如GPU中对图像处理都会有大量的并行运算。为更好的编写并发程序从设计之初Go语言就注重如何在编程语言层级上设计一个简洁安全高效的抽象模型让程序员专注于分解问题和组合方案而且不用被线程管理和信号互斥这些繁琐的操作分散精力。 首先要明确一个概念并发不是并行。并发更关注的是程序的设计层面并发的程序完全是可以顺序执行的只有在真正的多核CPU上才可能真正地同时运行。并行更关注的是程序的运行层面并行一般是简单的大量重复例如GPU中对图像处理都会有大量的并行运算。为更好的编写并发程序从设计之初Go语言就注重如何在编程语言层级上设计一个简洁安全高效的抽象模型让程序员专注于分解问题和组合方案而且不用被线程管理和信号互斥这些繁琐的操作分散精力。
在并发编程中对共享资源的正确访问需要精确的控制在目前的绝大多数语言中都是通过加锁等线程同步方案来解决这一困难问题而Go语言却另辟蹊径它将共享的值通过信道传递(实际上多个独立执行的线程很少主动共享资源)。在任意给定的时刻最好只有一个Goroutine能够拥有该资源。数据竞争从设计层面上就被杜绝了。为了提倡这种思考方式Go语言将其并发编程哲学化为一句口号 在并发编程中对共享资源的正确访问需要精确的控制在目前的绝大多数语言中都是通过加锁等线程同步方案来解决这一困难问题而Go语言却另辟蹊径它将共享的值通过Channel传递(实际上多个独立执行的线程很少主动共享资源)。在任意给定的时刻最好只有一个Goroutine能够拥有该资源。数据竞争从设计层面上就被杜绝了。为了提倡这种思考方式Go语言将其并发编程哲学化为一句口号
> Do not communicate by sharing memory; instead, share memory by communicating. > Do not communicate by sharing memory; instead, share memory by communicating.
> 不要通过共享内存来通信,而应通过通信来共享内存。 > 不要通过共享内存来通信,而应通过通信来共享内存。
这是更高层次的并发编程哲学(通过管道来传值是Go语言推荐的做法)。虽然像引用计数这类简单的并发问题通过原子操作或互斥锁就能很好地实现,但是通过信道来控制访问能够让你写出更简洁正确的程序。 这是更高层次的并发编程哲学(通过管道来传值是Go语言推荐的做法)。虽然像引用计数这类简单的并发问题通过原子操作或互斥锁就能很好地实现,但是通过Channel来控制访问能够让你写出更简洁正确的程序。
## 1.6.1 并发版本的Hello world ## 1.6.1 并发版本的Hello world
@ -66,7 +66,7 @@ func main() {
} }
``` ```
根据Go语言内存模型规范对于从无缓冲信道进行的接收,发生在对该信道进行的发送完成之前。因此,后台线程`<-done`接收操作完成之后,`main`线程的`done <- 1`发生操作才可能完成从而退出main、退出程序而此时打印工作已经完成了。 根据Go语言内存模型规范对于从无缓冲Channel进行的接收发生在对该Channel进行的发送完成之前。因此,后台线程`<-done`接收操作完成之后,`main`线程的`done <- 1`发生操作才可能完成从而退出main、退出程序而此时打印工作已经完成了。
上面的代码虽然可以正确同步但是对管道的缓存大小太敏感如果管道有缓存的话就无法保证main退出之前后台线程能正常打印了。更好的做法是将管道的发送和接收方向调换一下这样可以避免同步事件受管道缓存大小的影响 上面的代码虽然可以正确同步但是对管道的缓存大小太敏感如果管道有缓存的话就无法保证main退出之前后台线程能正常打印了。更好的做法是将管道的发送和接收方向调换一下这样可以避免同步事件受管道缓存大小的影响