From 0804a08d3acfecef3841742dda71a3c45a26cc98 Mon Sep 17 00:00:00 2001 From: sfw Date: Thu, 9 Aug 2018 19:45:49 +0800 Subject: [PATCH] fix typos --- ch1-basic/ch1-05-mem.md | 8 ++++---- ch1-basic/ch1-06-goroutine.md | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ch1-basic/ch1-05-mem.md b/ch1-basic/ch1-05-mem.md index 45db420..57b9983 100644 --- a/ch1-basic/ch1-05-mem.md +++ b/ch1-basic/ch1-05-mem.md @@ -308,7 +308,7 @@ func main() { 可保证打印出“hello, world”。该程序首先对`msg`进行写入,然后在`done`管道上发送同步信号,随后从`done`接收对应的同步信号,最后执行`println`函数。 -若在关闭信道后继续从中接收数据,接收者就会收到该信道返回的零值。因此在这个例子中,用`close(c)`关闭管道代替`done <- false`依然能保证该程序产生相同的行为。 +若在关闭Channel后继续从中接收数据,接收者就会收到该Channel返回的零值。因此在这个例子中,用`close(c)`关闭管道代替`done <- false`依然能保证该程序产生相同的行为。 ```go var done = make(chan bool) @@ -326,7 +326,7 @@ func main() { } ``` -**对于从无缓冲信道进行的接收,发生在对该信道进行的发送完成之前。** +**对于从无缓冲Channel进行的接收,发生在对该Channel进行的发送完成之前。** 基于上面这个规则可知,交换两个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的最大数目, 例如: diff --git a/ch1-basic/ch1-06-goroutine.md b/ch1-basic/ch1-06-goroutine.md index 3b3e999..6492a4f 100644 --- a/ch1-basic/ch1-06-goroutine.md +++ b/ch1-basic/ch1-06-goroutine.md @@ -4,13 +4,13 @@ Go语言最吸引人的地方是它内建的并发支持。Go语言并发体系 首先要明确一个概念:并发不是并行。并发更关注的是程序的设计层面,并发的程序完全是可以顺序执行的,只有在真正的多核CPU上才可能真正地同时运行。并行更关注的是程序的运行层面,并行一般是简单的大量重复,例如GPU中对图像处理都会有大量的并行运算。为更好的编写并发程序,从设计之初Go语言就注重如何在编程语言层级上设计一个简洁安全高效的抽象模型,让程序员专注于分解问题和组合方案,而且不用被线程管理和信号互斥这些繁琐的操作分散精力。 -在并发编程中,对共享资源的正确访问需要精确的控制,在目前的绝大多数语言中,都是通过加锁等线程同步方案来解决这一困难问题,而Go语言却另辟蹊径,它将共享的值通过信道传递(实际上多个独立执行的线程很少主动共享资源)。在任意给定的时刻,最好只有一个Goroutine能够拥有该资源。数据竞争从设计层面上就被杜绝了。为了提倡这种思考方式,Go语言将其并发编程哲学化为一句口号: +在并发编程中,对共享资源的正确访问需要精确的控制,在目前的绝大多数语言中,都是通过加锁等线程同步方案来解决这一困难问题,而Go语言却另辟蹊径,它将共享的值通过Channel传递(实际上多个独立执行的线程很少主动共享资源)。在任意给定的时刻,最好只有一个Goroutine能够拥有该资源。数据竞争从设计层面上就被杜绝了。为了提倡这种思考方式,Go语言将其并发编程哲学化为一句口号: > Do not communicate by sharing memory; instead, share memory by communicating. > 不要通过共享内存来通信,而应通过通信来共享内存。 -这是更高层次的并发编程哲学(通过管道来传值是Go语言推荐的做法)。虽然像引用计数这类简单的并发问题通过原子操作或互斥锁就能很好地实现,但是通过信道来控制访问能够让你写出更简洁正确的程序。 +这是更高层次的并发编程哲学(通过管道来传值是Go语言推荐的做法)。虽然像引用计数这类简单的并发问题通过原子操作或互斥锁就能很好地实现,但是通过Channel来控制访问能够让你写出更简洁正确的程序。 ## 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退出之前后台线程能正常打印了。更好的做法是将管道的发送和接收方向调换一下,这样可以避免同步事件受管道缓存大小的影响: