This commit is contained in:
Xargin 2016-09-30 21:56:19 +08:00
parent 25eec00705
commit 853e2ad052
6 changed files with 10 additions and 10 deletions

View File

@ -2,7 +2,7 @@
拥有函数名的函数只能在包级语法块中被声明通过函数字面量function literal我们可绕过这一限制在任何表达式中表示一个函数值。函数字面量的语法和函数声明相似区别在于func关键字后没有函数名。函数值字面量是一种表达式它的值被成为匿名函数anonymous function 拥有函数名的函数只能在包级语法块中被声明通过函数字面量function literal我们可绕过这一限制在任何表达式中表示一个函数值。函数字面量的语法和函数声明相似区别在于func关键字后没有函数名。函数值字面量是一种表达式它的值被成为匿名函数anonymous function
函数字面量允许我们在使用函数时再定义它。通过这种技巧我们可以改写之前对strings.Map的调用 函数字面量允许我们在使用函数时再定义它。通过这种技巧我们可以改写之前对strings.Map的调用
```Go ```Go
strings.Map(func(r rune) rune { return r + 1 }, "HAL-9000") strings.Map(func(r rune) rune { return r + 1 }, "HAL-9000")

View File

@ -8,9 +8,9 @@ func squarer(out, in chan int)
func printer(in chan int) func printer(in chan int)
``` ```
其中squarer计算平方的函数在两个串联Channels的中间因此拥有两个channels类型的参数一个用于输入一个用于输出。每个channels都用有相同的类型但是它们的使用方式一个只用于接收另一个只用于发送。参数的名字in和out已经明确表示了这个意图但是并无法保证squarer函数向一个in参数对应的channels发送数据或者从一个out参数对应的channels接收数据。 其中squarer计算平方的函数在两个串联Channels的中间因此拥有两个channels类型的参数一个用于输入一个用于输出。每个channels都用有相同的类型但是它们的使用方式一个只用于接收另一个只用于发送。参数的名字in和out已经明确表示了这个意图但是并无法保证squarer函数向一个in参数对应的channels发送数据或者从一个out参数对应的channels接收数据。
这种场景是典型的。当一个channel作为一个函数参数,它一般总是被专门用于只发送或者只接收。 这种场景是典型的。当一个channel作为一个函数参数,它一般总是被专门用于只发送或者只接收。
为了表明这种意图并防止被滥用Go语言的类型系统提供了单方向的channel类型分别用于只发送或只接收的channel。类型`chan<- int`表示一个只发送int的channel只能发送不能接收相反类型`<-chan int`表示一个只接收int的channel只能接收不能发送箭头`<-`和关键字chan的相对位置表明了channel的方向这种限制将在编译期检测 为了表明这种意图并防止被滥用Go语言的类型系统提供了单方向的channel类型分别用于只发送或只接收的channel。类型`chan<- int`表示一个只发送int的channel只能发送不能接收相反类型`<-chan int`表示一个只接收int的channel只能接收不能发送箭头`<-`和关键字chan的相对位置表明了channel的方向这种限制将在编译期检测

View File

@ -51,7 +51,7 @@ fmt.Println(<-ch) // "B"
fmt.Println(<-ch) // "C" fmt.Println(<-ch) // "C"
``` ```
在这个例子中发送和接收操作都发生在同一个goroutine中但是在真的程序中它们一般由不同的goroutine执行。Go语言新手有时候会将一个带缓存的channel当作同一个goroutine中的队列使用虽然语法看似简单但实际上这是一个错误。Channel和goroutine的调度器机制是紧密相连的一个发送操作——或许是整个程序——可能会永远阻塞。如果你只是需要一个简单的队列使用slice就可以了。 在这个例子中发送和接收操作都发生在同一个goroutine中但是在真的程序中它们一般由不同的goroutine执行。Go语言新手有时候会将一个带缓存的channel当作同一个goroutine中的队列使用虽然语法看似简单但实际上这是一个错误。Channel和goroutine的调度器机制是紧密相连的一个发送操作——或许是整个程序——可能会永远阻塞。如果你只是需要一个简单的队列使用slice就可以了。
下面的例子展示了一个使用了带缓存channel的应用。它并发地向三个镜像站点发出请求三个镜像站点分散在不同的地理位置。它们分别将收到的响应发送到带缓存channel最后接收者只接收第一个收到的响应也就是最快的那个响应。因此mirroredQuery函数可能在另外两个响应慢的镜像站点响应之前就返回了结果。顺便说一下多个goroutines并发地向同一个channel发送数据或从同一个channel接收数据都是常见的用法。 下面的例子展示了一个使用了带缓存channel的应用。它并发地向三个镜像站点发出请求三个镜像站点分散在不同的地理位置。它们分别将收到的响应发送到带缓存channel最后接收者只接收第一个收到的响应也就是最快的那个响应。因此mirroredQuery函数可能在另外两个响应慢的镜像站点响应之前就返回了结果。顺便说一下多个goroutines并发地向同一个channel发送数据或从同一个channel接收数据都是常见的用法。
@ -73,7 +73,7 @@ func request(hostname string) (response string) { /* ... */ }
Channel的缓存也可能影响程序的性能。想象一家蛋糕店有三个厨师一个烘焙一个上糖衣还有一个将每个蛋糕传递到它下一个厨师在生产线。在狭小的厨房空间环境每个厨师在完成蛋糕后必须等待下一个厨师已经准备好接受它这类似于在一个无缓存的channel上进行沟通。 Channel的缓存也可能影响程序的性能。想象一家蛋糕店有三个厨师一个烘焙一个上糖衣还有一个将每个蛋糕传递到它下一个厨师在生产线。在狭小的厨房空间环境每个厨师在完成蛋糕后必须等待下一个厨师已经准备好接受它这类似于在一个无缓存的channel上进行沟通。
如果在每个厨师之间有一个放置一个蛋糕的额外空间那么每个厨师就可以将一个完成的蛋糕临时放在那里而马上进入下一个蛋糕在制作中这类似于将channel的缓存队列的容量设置为1。只要每个厨师的平均工作效率相近那么其中大部分的传输工作将是迅速的个体之间细小的效率差异将在交接过程中弥补。如果厨师之间有更大的额外空间——也是就更大容量的缓存队列——将可以在不停止生产线的前提下消除更大的效率波动例如一个厨师可以短暂地休息然后加快赶上进度而不影响其他人。 如果在每个厨师之间有一个放置一个蛋糕的额外空间那么每个厨师就可以将一个完成的蛋糕临时放在那里而马上进入下一个蛋糕在制作中这类似于将channel的缓存队列的容量设置为1。只要每个厨师的平均工作效率相近那么其中大部分的传输工作将是迅速的个体之间细小的效率差异将在交接过程中弥补。如果厨师之间有更大的额外空间——也是就更大容量的缓存队列——将可以在不停止生产线的前提下消除更大的效率波动例如一个厨师可以短暂地休息然后加快赶上进度而不影响其他人。
另一方面,如果生产线的前期阶段一直快于后续阶段,那么它们之间的缓存在大部分时间都将是满的。相反,如果后续阶段比前期阶段更快,那么它们之间的缓存在大部分时间都将是空的。对于这类场景,额外的缓存并没有带来任何好处。 另一方面,如果生产线的前期阶段一直快于后续阶段,那么它们之间的缓存在大部分时间都将是满的。相反,如果后续阶段比前期阶段更快,那么它们之间的缓存在大部分时间都将是空的。对于这类场景,额外的缓存并没有带来任何好处。

View File

@ -8,9 +8,9 @@
ch := make(chan int) // ch has type 'chan int' ch := make(chan int) // ch has type 'chan int'
``` ```
和map类似channel也一个对应make创建的底层数据结构的引用。当我们复制一个channel或用于函数参数传递时我们只是拷贝了一个channel引用因此调用者被调用者将引用同一个channel对象。和其它的引用类型一样channel的零值也是nil。 和map类似channel也一个对应make创建的底层数据结构的引用。当我们复制一个channel或用于函数参数传递时我们只是拷贝了一个channel引用因此调用者被调用者将引用同一个channel对象。和其它的引用类型一样channel的零值也是nil。
两个相同类型的channel可以使用==运算符比较。如果两个channel引用的是相的对象那么比较的结果为真。一个channel也可以和nil进行比较。 两个相同类型的channel可以使用==运算符比较。如果两个channel引用的是相的对象那么比较的结果为真。一个channel也可以和nil进行比较。
一个channel有发送和接受两个主要操作都是通信行为。一个发送语句将一个值从一个goroutine通过channel发送到另一个执行接收操作的goroutine。发送和接收两个操作都是用`<-`运算符在发送语句中`<-`运算符分割channel和要发送的值在接收语句中`<-`运算符写在channel对象之前一个不使用接收结果的接收操作也是合法的 一个channel有发送和接受两个主要操作都是通信行为。一个发送语句将一个值从一个goroutine通过channel发送到另一个执行接收操作的goroutine。发送和接收两个操作都是用`<-`运算符在发送语句中`<-`运算符分割channel和要发送的值在接收语句中`<-`运算符写在channel对象之前一个不使用接收结果的接收操作也是合法的
@ -20,7 +20,7 @@ x = <-ch // a receive expression in an assignment statement
<-ch // a receive statement; result is discarded <-ch // a receive statement; result is discarded
``` ```
Channel还支持close操作用于关闭channel随后对基于该channel的任何发送操作都将导致panic异常。对一个已经被close过的channel行接收操作依然可以接受到之前已经成功发送的数据如果channel中已经没有数据的话讲产生一个零值的数据。 Channel还支持close操作用于关闭channel随后对基于该channel的任何发送操作都将导致panic异常。对一个已经被close过的channel行接收操作依然可以接受到之前已经成功发送的数据如果channel中已经没有数据的话讲产生一个零值的数据。
使用内置的close函数就可以关闭一个channel 使用内置的close函数就可以关闭一个channel

View File

@ -98,7 +98,7 @@ func makeThumbnails4(filenames []string) error {
} }
``` ```
这个程序有一个微的bug。当它遇到第一个非nil的error时会直接将error返回到调用方使得没有一个goroutine去排空errors channel。这样剩下的worker goroutine在向这个channel中发送值时都会永远地阻塞下去并且永远都不会退出。这种情况叫做goroutine泄露(§8.4.4)可能会导致整个程序卡住或者跑出out of memory的错误。 这个程序有一个微的bug。当它遇到第一个非nil的error时会直接将error返回到调用方使得没有一个goroutine去排空errors channel。这样剩下的worker goroutine在向这个channel中发送值时都会永远地阻塞下去并且永远都不会退出。这种情况叫做goroutine泄露(§8.4.4)可能会导致整个程序卡住或者跑出out of memory的错误。
最简单的解决办法就是用一个具有合适大小的buffered channel这样这些worker goroutine向channel中发送错误时就不会被阻塞。(一个可选的解决办法是创建一个另外的goroutine当main goroutine返回第一个错误的同时去排空channel) 最简单的解决办法就是用一个具有合适大小的buffered channel这样这些worker goroutine向channel中发送错误时就不会被阻塞。(一个可选的解决办法是创建一个另外的goroutine当main goroutine返回第一个错误的同时去排空channel)

View File

@ -64,7 +64,7 @@ func main() {
``` ```
下面这个例子更微。ch这个channel的buffer大小是1所以会交替的为空或为满所以只有一个case可以进行下去无论i是奇数或者偶数它都会打印0 2 4 6 8。 下面这个例子更微。ch这个channel的buffer大小是1所以会交替的为空或为满所以只有一个case可以进行下去无论i是奇数或者偶数它都会打印0 2 4 6 8。
```go ```go
ch := make(chan int, 1) ch := make(chan int, 1)