From f2a3e873feb31a192374546a3fdd7d979a6ae156 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=80=80?= Date: Sun, 20 Nov 2016 11:22:57 +0800 Subject: [PATCH] Fix 14.5, 14.6, 15.1 (#283) * Update 07.3.md * Update 07.3.md * Update 07.6.md * fix few problems * Update 10.6 and 10.8 * Update 11.6 and 11.7 * fix some problems before Chap13 * add a dot * Update 13.9 * little improve * Thoughts about time.After * Thoughts about time.After * Fix 14.6,15.1 --- eBook/14.5.md | 19 +++++++++++++++++-- eBook/14.6.md | 2 +- eBook/15.1.md | 10 +++++----- eBook/examples/chapter_15/server.go | 4 ++-- .../examples/chapter_15/simple_tcp_server.go | 4 ++-- .../chapter_15/simple_tcp_server_v1.go | 2 +- 6 files changed, 28 insertions(+), 13 deletions(-) diff --git a/eBook/14.5.md b/eBook/14.5.md index 095adb0..23e97af 100644 --- a/eBook/14.5.md +++ b/eBook/14.5.md @@ -137,7 +137,7 @@ select { 第二种形式:取消耗时很长的同步调用 -也可以使用 `time.After()` 函数替换 `timeout-channel`。可以在 `select` 中使用以发送信号超时或停止协程的执行。以下代码,在 `timeoutNs` 纳秒后执行 `select` 的 `timeout` 分支时,`client.Call` 不会给通道 `ch` 返回值: +也可以使用 `time.After()` 函数替换 `timeout-channel`。可以在 `select` 中通过 `time.After()` 发送的超时信号来停止协程的执行。以下代码,在 `timeoutNs` 纳秒后执行 `select` 的 `timeout` 分支后,执行`client.Call` 的协程也随之结束,不会给通道 `ch` 返回值: ```go ch := make(chan error, 1) @@ -151,7 +151,22 @@ case <-time.After(timeoutNs): } ``` -注意缓冲大小设置为 1 是必要的,可以避免协程死锁以及确保超时的通道可以被垃圾回收。 +注意缓冲大小设置为 1 是必要的,可以避免协程死锁以及确保超时的通道可以被垃圾回收。此外,需要注意在有多个 `case` 符合条件时, `select` 对 `case` 的选择是伪随机的,如果上面的代码稍作修改如下,则 `select` 语句可能不会在定时器超时信号到来时立刻选中 `time.After(timeoutNs)` 对应的 `case`,因此协程可能不会严格按照定时器设置的时间结束。 + +```go +ch := make(chan int, 1) +go func() { for { ch <- 1 } } () +L: +for { + select { + case <-ch: + // do something + case <-time.After(timeoutNs): + // call timed out + break L + } +} +``` 第三种形式:假设程序从多个复制的数据库同时读取。只需要一个答案,需要接收首先到达的答案,`Query` 函数获取数据库的连接切片并请求。并行请求每一个数据库并返回收到的第一个响应: diff --git a/eBook/14.6.md b/eBook/14.6.md index 8ab2b92..bd32d1b 100644 --- a/eBook/14.6.md +++ b/eBook/14.6.md @@ -21,7 +21,7 @@ func safelyDo(work *Work) { 上边的代码,如果 `do(work)` 发生 panic,错误会被记录且协程会退出并释放,而其他协程不受影响。 -因为 `recover` 总是返回 `nil`,除非直接在 `defer` 修饰的函数中调用,`defer` 修饰的代码可以调用那些自身可以使用 `panic` 和 `recover` 避免失败的库例程(库函数)。举例,`safelyDo()` 中 `deffer` 修饰的函数可能在调用 `recover` 之前就调用了一个 `logging` 函数,`panicking` 状态不会影响 `logging` 代码的运行。因为加入了恢复模式,函数 `do`(以及它调用的任何东西)可以通过调用 `panic` 来摆脱不好的情况。但是恢复是在 `panicking` 的协程内部的:不能被另外一个协程恢复。 +因为 `recover` 总是返回 `nil`,除非直接在 `defer` 修饰的函数中调用,`defer` 修饰的代码可以调用那些自身可以使用 `panic` 和 `recover` 避免失败的库例程(库函数)。举例,`safelyDo()` 中 `defer` 修饰的函数可能在调用 `recover` 之前就调用了一个 `logging` 函数,`panicking` 状态不会影响 `logging` 代码的运行。因为加入了恢复模式,函数 `do`(以及它调用的任何东西)可以通过调用 `panic` 来摆脱不好的情况。但是恢复是在 `panicking` 的协程内部的:不能被另外一个协程恢复。 ## 链接 diff --git a/eBook/15.1.md b/eBook/15.1.md index e89804b..c0ce583 100644 --- a/eBook/15.1.md +++ b/eBook/15.1.md @@ -35,18 +35,18 @@ func main() { func doServerStuff(conn net.Conn) { for { buf := make([]byte, 512) - _, err := conn.Read(buf) + len, err := conn.Read(buf) if err != nil { fmt.Println("Error reading", err.Error()) return //终止程序 } - fmt.Printf("Received data: %v", string(buf)) + fmt.Printf("Received data: %v", string(buf[:len])) } } ``` -我们在`main()`创建了一个`net.Listener`的变量,他是一个服务器的基本函数:用来监听和接收来自客户端的请求(来自localhost即IP地址为127.0.0.1端口为50000基于TCP协议)。这个`Listen()`函数可以返回一个`error`类型的错误变量。用一个无限for循环的`listener.Accept()`来等待客户端的请求。客户端的请求将产生一个`net.Conn`类型的连接变量。然后一个独立的携程使用这个连接执行`doServerStuff()`,开始使用一个512字节的缓冲`data`来读取客户端发送来的数据并且把它们打印到服务器的终端;当客户端发送的所有数据都被读取完成时,携程就结束了。这段程序会为每一个客户端连接创建一个独立的携程。必须先运行服务器代码,再运行客户端代码。 +我们在`main()`创建了一个`net.Listener`的变量,他是一个服务器的基本函数:用来监听和接收来自客户端的请求(来自localhost即IP地址为127.0.0.1端口为50000基于TCP协议)。这个`Listen()`函数可以返回一个`error`类型的错误变量。用一个无限for循环的`listener.Accept()`来等待客户端的请求。客户端的请求将产生一个`net.Conn`类型的连接变量。然后一个独立的携程使用这个连接执行`doServerStuff()`,开始使用一个512字节的缓冲`data`来读取客户端发送来的数据并且把它们打印到服务器的终端,`len`获取客户端发送的数据字节数;当客户端发送的所有数据都被读取完成时,携程就结束了。这段程序会为每一个客户端连接创建一个独立的携程。必须先运行服务器代码,再运行客户端代码。 客户端代码写在另外一个文件client.go中: @@ -106,7 +106,7 @@ func main() { 然后开启2个或者3个独立的控制台窗口,然后分别输入client回车启动客户端程序 -以下是服务器的输出(在移除掉512字节的字符串中内容为空的区域后): +以下是服务器的输出: ``` Starting the Server ... Received data: IVO says: Hi Server, what's up ? @@ -229,7 +229,7 @@ func main() { func initServer(hostAndPort string) *net.TCPListener { serverAddr, err := net.ResolveTCPAddr("tcp", hostAndPort) checkError(err, "Resolving address:port failed: '"+hostAndPort+"'") - listener, err := net.ListenTCP("tcp", serverAddr) + listener, err := net.Listen("tcp", serverAddr) checkError(err, "ListenTCP: ") println("Listening to: ", listener.Addr().String()) return listener diff --git a/eBook/examples/chapter_15/server.go b/eBook/examples/chapter_15/server.go index 39c32b0..38910a5 100644 --- a/eBook/examples/chapter_15/server.go +++ b/eBook/examples/chapter_15/server.go @@ -27,11 +27,11 @@ func main() { func doServerStuff(conn net.Conn) { for { buf := make([]byte, 512) - _, err := conn.Read(buf) + len, err := conn.Read(buf) if err != nil { fmt.Println("Error reading", err.Error()) return //终止程序 } - fmt.Printf("Received data: %v", string(buf)) + fmt.Printf("Received data: %v", string(buf[:len])) } } diff --git a/eBook/examples/chapter_15/simple_tcp_server.go b/eBook/examples/chapter_15/simple_tcp_server.go index 8e56d6e..8cc0e79 100644 --- a/eBook/examples/chapter_15/simple_tcp_server.go +++ b/eBook/examples/chapter_15/simple_tcp_server.go @@ -4,8 +4,8 @@ package main import ( "flag" "fmt" - "os" "net" + "os" ) const maxRead = 25 @@ -27,7 +27,7 @@ func main() { func initServer(hostAndPort string) *net.TCPListener { serverAddr, err := net.ResolveTCPAddr("tcp", hostAndPort) checkError(err, "Resolving address:port failed: '"+hostAndPort+"'") - listener, err := net.ListenTCP("tcp", serverAddr) + listener, err := net.Listen("tcp", serverAddr) checkError(err, "ListenTCP: ") println("Listening to: ", listener.Addr().String()) return listener diff --git a/eBook/examples/chapter_15/simple_tcp_server_v1.go b/eBook/examples/chapter_15/simple_tcp_server_v1.go index 6ad96aa..963b1d0 100644 --- a/eBook/examples/chapter_15/simple_tcp_server_v1.go +++ b/eBook/examples/chapter_15/simple_tcp_server_v1.go @@ -26,7 +26,7 @@ func main() { func initServer(hostAndPort string) *net.TCPListener { serverAddr, err := net.ResolveTCPAddr("tcp", hostAndPort) checkError(err, "Resolving address:port failed: '"+hostAndPort+"'") - listener, err := net.ListenTCP("tcp", serverAddr) + listener, err := net.Listen("tcp", serverAddr) checkError(err, "ListenTCP: ") println("Listening to: ", listener.Addr().String()) return listener