mirror of
https://github.com/unknwon/the-way-to-go_ZH_CN.git
synced 2025-08-12 05:33:04 +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
|
```go
|
||||||
ch := make(chan error, 1)
|
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` 函数获取数据库的连接切片并请求。并行请求每一个数据库并返回收到的第一个响应:
|
第三种形式:假设程序从多个复制的数据库同时读取。只需要一个答案,需要接收首先到达的答案,`Query` 函数获取数据库的连接切片并请求。并行请求每一个数据库并返回收到的第一个响应:
|
||||||
|
|
||||||
|
@@ -21,7 +21,7 @@ func safelyDo(work *Work) {
|
|||||||
|
|
||||||
上边的代码,如果 `do(work)` 发生 panic,错误会被记录且协程会退出并释放,而其他协程不受影响。
|
上边的代码,如果 `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) {
|
func doServerStuff(conn net.Conn) {
|
||||||
for {
|
for {
|
||||||
buf := make([]byte, 512)
|
buf := make([]byte, 512)
|
||||||
_, err := conn.Read(buf)
|
len, err := conn.Read(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error reading", err.Error())
|
fmt.Println("Error reading", err.Error())
|
||||||
return //终止程序
|
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中:
|
客户端代码写在另外一个文件client.go中:
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ func main() {
|
|||||||
|
|
||||||
然后开启2个或者3个独立的控制台窗口,然后分别输入client回车启动客户端程序
|
然后开启2个或者3个独立的控制台窗口,然后分别输入client回车启动客户端程序
|
||||||
|
|
||||||
以下是服务器的输出(在移除掉512字节的字符串中内容为空的区域后):
|
以下是服务器的输出:
|
||||||
```
|
```
|
||||||
Starting the Server ...
|
Starting the Server ...
|
||||||
Received data: IVO says: Hi Server, what's up ?
|
Received data: IVO says: Hi Server, what's up ?
|
||||||
@@ -229,7 +229,7 @@ func main() {
|
|||||||
func initServer(hostAndPort string) *net.TCPListener {
|
func initServer(hostAndPort string) *net.TCPListener {
|
||||||
serverAddr, err := net.ResolveTCPAddr("tcp", hostAndPort)
|
serverAddr, err := net.ResolveTCPAddr("tcp", hostAndPort)
|
||||||
checkError(err, "Resolving address:port failed: '"+hostAndPort+"'")
|
checkError(err, "Resolving address:port failed: '"+hostAndPort+"'")
|
||||||
listener, err := net.ListenTCP("tcp", serverAddr)
|
listener, err := net.Listen("tcp", serverAddr)
|
||||||
checkError(err, "ListenTCP: ")
|
checkError(err, "ListenTCP: ")
|
||||||
println("Listening to: ", listener.Addr().String())
|
println("Listening to: ", listener.Addr().String())
|
||||||
return listener
|
return listener
|
||||||
|
@@ -27,11 +27,11 @@ func main() {
|
|||||||
func doServerStuff(conn net.Conn) {
|
func doServerStuff(conn net.Conn) {
|
||||||
for {
|
for {
|
||||||
buf := make([]byte, 512)
|
buf := make([]byte, 512)
|
||||||
_, err := conn.Read(buf)
|
len, err := conn.Read(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error reading", err.Error())
|
fmt.Println("Error reading", err.Error())
|
||||||
return //终止程序
|
return //终止程序
|
||||||
}
|
}
|
||||||
fmt.Printf("Received data: %v", string(buf))
|
fmt.Printf("Received data: %v", string(buf[:len]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,8 +4,8 @@ package main
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
const maxRead = 25
|
const maxRead = 25
|
||||||
@@ -27,7 +27,7 @@ func main() {
|
|||||||
func initServer(hostAndPort string) *net.TCPListener {
|
func initServer(hostAndPort string) *net.TCPListener {
|
||||||
serverAddr, err := net.ResolveTCPAddr("tcp", hostAndPort)
|
serverAddr, err := net.ResolveTCPAddr("tcp", hostAndPort)
|
||||||
checkError(err, "Resolving address:port failed: '"+hostAndPort+"'")
|
checkError(err, "Resolving address:port failed: '"+hostAndPort+"'")
|
||||||
listener, err := net.ListenTCP("tcp", serverAddr)
|
listener, err := net.Listen("tcp", serverAddr)
|
||||||
checkError(err, "ListenTCP: ")
|
checkError(err, "ListenTCP: ")
|
||||||
println("Listening to: ", listener.Addr().String())
|
println("Listening to: ", listener.Addr().String())
|
||||||
return listener
|
return listener
|
||||||
|
@@ -26,7 +26,7 @@ func main() {
|
|||||||
func initServer(hostAndPort string) *net.TCPListener {
|
func initServer(hostAndPort string) *net.TCPListener {
|
||||||
serverAddr, err := net.ResolveTCPAddr("tcp", hostAndPort)
|
serverAddr, err := net.ResolveTCPAddr("tcp", hostAndPort)
|
||||||
checkError(err, "Resolving address:port failed: '"+hostAndPort+"'")
|
checkError(err, "Resolving address:port failed: '"+hostAndPort+"'")
|
||||||
listener, err := net.ListenTCP("tcp", serverAddr)
|
listener, err := net.Listen("tcp", serverAddr)
|
||||||
checkError(err, "ListenTCP: ")
|
checkError(err, "ListenTCP: ")
|
||||||
println("Listening to: ", listener.Addr().String())
|
println("Listening to: ", listener.Addr().String())
|
||||||
return listener
|
return listener
|
||||||
|
Reference in New Issue
Block a user