mirror of
https://github.com/unknwon/the-way-to-go_ZH_CN.git
synced 2025-08-11 23:08:34 +08:00
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
This commit is contained in:
@@ -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` 函数获取数据库的连接切片并请求。并行请求每一个数据库并返回收到的第一个响应:
|
||||
|
||||
|
@@ -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` 的协程内部的:不能被另外一个协程恢复。
|
||||
|
||||
|
||||
## 链接
|
||||
|
@@ -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
|
||||
|
@@ -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]))
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user