diff --git a/ch1-basic/ch1-06-goroutine.md b/ch1-basic/ch1-06-goroutine.md index 1bff18b..3b3e999 100644 --- a/ch1-basic/ch1-06-goroutine.md +++ b/ch1-basic/ch1-06-goroutine.md @@ -387,7 +387,7 @@ func (fs gatefs) Lstat(p string) (os.FileInfo, error) { 采用并发编程的动机有很多:并发编程可以简化问题,比如一类问题对应一个处理线程会更简单;并发编程还可以提升性能,在一个多核CPU上开2个线程一般会比开1个线程快一些。其实对于提升性能而言,程序并不是简单地运行速度快就表示用户体验好的;很多时候程序能快速响应用户请求才是最重要的,当没有用户请求需要处理的时候才合适处理一些低优先级的后台任务。 -假设我们想快速地检索“golang”相关的主题,我们可能会同时打开Bing、Google或百度等多个检索引擎。当某个检索最先返回结果后,就可以关闭其它检索页面了。因为受限于网络环境和检索引擎算法的影响,某些检索引擎可能很快返回检索结果,某些检索引擎也可能遇到等到他们公司倒闭也没有完成检索的情况。我们可以采用类似的策略来编写这个程序: +假设我们想快速地搜索“golang”相关的主题,我们可能会同时打开Bing、Google或百度等多个检索引擎。当某个搜索最先返回结果后,就可以关闭其它搜索页面了。因为受网络环境和搜索引擎算法的影响,某些搜索引擎可能很快返回搜索结果,某些搜索引擎也可能等到他们公司倒闭也没有完成搜索。我们可以采用类似的策略来编写这个程序: ```go func main() { @@ -407,7 +407,7 @@ func main() { } ``` -首先,我们创建了一个带缓存的管道,管道的缓存数目要足够大,保证不会因为缓存的容量引起不必要的阻塞。然后我们开启了多个后台线程,分别向不同的检索引擎提交检索请求。当任意一个检索引擎最先有结果之后,都会马上将结果发到管道中(因为管道带了足够的缓存,这个过程不会阻塞)。但是最终我们只从管道取第一个结果,也就是最先返回的结果。 +首先,我们创建了一个带缓存的管道,管道的缓存数目要足够大,保证不会因为缓存的容量引起不必要的阻塞。然后我们开启了多个后台线程,分别向不同的搜索引擎提交搜索请求。当任意一个搜索引擎最先有结果之后,都会马上将结果发到管道中(因为管道带了足够的缓存,这个过程不会阻塞)。但是最终我们只从管道取第一个结果,也就是最先返回的结果。 通过适当开启一些冗余的线程,尝试用不同途径去解决同样的问题,最终以赢者为王的方式提升了程序的相应性能。 @@ -469,11 +469,11 @@ func main() { 我们先是调用`GenerateNatural()`生成最原始的从2开始的自然数序列。然后开始一个100次迭代的循环,希望生成100个素数。在每次循环迭代开始的时候,管道中的第一个数必定是素数,我们先读取并打印这个素数。然后基于管道中剩余的数列,并以当前取出的素数为筛子过滤后面的素数。不同的素数筛子对应的管道是串联在一起的。 -素数筛展示了一种优雅的并发程序结构。但是因为每个并发体处理的任务粒度太细微,程序整体的性能并不理想。对于细力度的并发程序,CSP模型中固有的消息传递的代价太高了(多线程并发模型同样要面临线程启动的代价)。 +素数筛展示了一种优雅的并发程序结构。但是因为每个并发体处理的任务粒度太细微,程序整体的性能并不理想。对于细粒度的并发程序,CSP模型中固有的消息传递的代价太高了(多线程并发模型同样要面临线程启动的代价)。 ## 1.6.7 并发的安全退出 -有时候我们需要通知goroutine停止它正在干的事情,特别是当它工作在错误的方向上的时候。Go语言并没有提供在一个直接终止Goroutine的方法,由于这样会导致goroutine之间的共享变量落在未定义的状态上。但是如果我们想要退出两个或者任意多个Goroutine怎么办呢? +有时候我们需要通知goroutine停止它正在干的事情,特别是当它工作在错误的方向上的时候。Go语言并没有提供在一个直接终止Goroutine的方法,由于这样会导致goroutine之间的共享变量处在未定义的状态上。但是如果我们想要退出两个或者任意多个Goroutine怎么办呢? Go语言中不同Goroutine之间主要依靠管道进行通信和同步。要同时处理多个管道的发送或接收操作,我们需要使用`select`关键字(这个关键字和网络编程中的`select`函数的行为类似)。当`select`有多个分支时,会随机选择一个可用的管道分支,如果没有可用的管道分支则选择`default`分支,否则会一直保存阻塞状态。 @@ -508,7 +508,7 @@ func main() { } ``` -当有多个管道均可操作时,`select`会随机选择一个管道。基于该特性我们可以用`select`实现一个生成随机数列的程序: +当有多个管道均可操作时,`select`会随机选择一个管道。基于该特性我们可以用`select`实现一个生成随机数序列的程序: ```go func main() { @@ -649,7 +649,7 @@ func main() { 当并发体超时或`main`主动停止工作者Goroutine时,每个工作者都可以安全退出。 -Go语言是带内存自动回收的特性,因此内存一般不会泄漏。在前面素数筛的例子中,`GenerateNatural`和`PrimeFilter`函数内部都启动了新的Goroutine,当`main`函数不再使用管道时后台Goroutine有泄漏的风险。我们可以通过`context`包来避免这个问题,下面是改进的素数筛实现: +Go语言是带内存自动回收特性的,因此内存一般不会泄漏。在前面素数筛的例子中,`GenerateNatural`和`PrimeFilter`函数内部都启动了新的Goroutine,当`main`函数不再使用管道时后台Goroutine有泄漏的风险。我们可以通过`context`包来避免这个问题,下面是改进的素数筛实现: ```go // 返回生成自然数序列的管道: 2, 3, 4, ...