Skip to content

Commit

Permalink
Merge branch 'master' into 10.7.1
Browse files Browse the repository at this point in the history
  • Loading branch information
hantmac committed Apr 19, 2019
2 parents 66964c0 + 0185eca commit 0f1bf6f
Show file tree
Hide file tree
Showing 4 changed files with 246 additions and 4 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,9 @@
- [10.5.2 sync.RWMutex类型](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter10/10.5.2.md)
- [10.5.3 通过goroutine共享内存](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter10/10.5.3.md)
- [10.6 竞争状态](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter10/10.6.md)
- [10.7.1 context使用的高级示例](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter10/10.7.1.md)

- [10.7 关于context包](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter10/10.7.md)
- [10.7.1 context使用的高级示例](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter10/10.7.1.md)
- [chapter 11 代码测试,优化以及分析](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter11/11.0.md)
- [11.1 本章使用的Go版本](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter11/11.1.md)
- [11.1.1 1.10和1.9的版本对比](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter11/11.1.1.md)
Expand Down
153 changes: 153 additions & 0 deletions eBook/chapter10/10.7.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# 关于context包

`context` 包的主要用途是定义 `Context` 类型和支持 **取消**!是的,您没听错;`context` 包的主要用途是支持取消操作,因为有时为了某种原因,您想要废弃正在做的操作。然而,能提供一些关于您取消决定的额外信息是非常有帮助的。`context` 包就能让您做到这点!

如果您浏览一下 `context` 包点源码,就会认识到它的实现是相当的简单——甚至 `Context` 类型的实现也非常简单,而 `context` 包是非常重要的。

> *`context` 包以前是作为外部 Go 包存在,但在 Go 1.7版本,它第一次作为标准 Go 包出现。因此,如果您有个较老的 Go 版本,再没先下载 `context` 包前,您不能跟着这节学习。*
`Context` 类型是一个有四个方法的接口,方法名为 `Deadline()``Done()``Err()``Value()`。好消息是您不需要实现 `Context` 接口的所有方法——您只需要使用如 `context.WithCancel()``context.WithTimeout()` 函数修改 `Context` 变量就行。

下面是使用 `context` 包的示例,用 `simpleContext.go` 文件源码,分六部分来介绍。

`simpleContext.go` 的第一部分代码如下:

```go
package main

import (
"context"
"fmt"
"os"
"strconv"
"time"
)
```

`simpleContext.go` 的第二部分如下:

```go
func f1(t int) {
c1 := context.Background()
c1, cancel := context.WithCancel(c1)
defer cancel()

go func(){
time.Sleep(4 * time.Second)
cancel()
}()
```
`f1()` 函数只需要一个时延参数,因为其他的都定义在函数里了。注意 `cancel` 变量的类型是 `context.CancelFunc`
您需要调用 `context.Background()` 函数来初始化一个空 `Context` 参数。`context.WithCancel()` 函数使用一个存在的 `Context` 创建一个子类并执行取消操作。`context.WithCancel()` 函数也会创建一个 `Done` 通道,如上面的代码所示当 `cancel()` 函数被调用时,或者当父 context 的 `Done` 通道关闭时,它会被关闭。
`simpleContext.go` 的第三部分包含 `f1()` 函数的其余部分:
```go
select {
case <- c1.Done():
fmt.Println("f1():", c1.Err())
return
case r := <-time.After(time.Duration(t) * time.Second):
fmt.Println("f1():", r)
}
return
}
```

这里您看到了 `Context` 变量的 `Done()` 函数的使用。当这个函数被调用时,您有一个取消操作。`Context.Done()` 的返回值是一个通道,否则您就不能在 `select` 语句中使用它了。

`simpleContext.go` 的第四部分如下:

```go
func f2(t int) {
c2 := context.Background()
c2, cancel := context.WithTimeout(c2, time.Duration(t)*time.Second)
defer cancel()

go func(){
time.Sleep(4 * time.Second)
cancel()
}()

select {
case <-c2.Done():
fmt.Println("f2():", c2.Err())
return
case r := <-time.After(time.Duration(t)*time.Second):
fmt.Println("f2():", r)
}
return
}
```

这部分展示了 `context.WithTimeout()` 函数的使用,它需要两个参数:`Context` 参数和 `time.Duration` 参数。当超时到达时,`cancel()` 函数自动调用。

`simpleContext.go` 的第五部分如下:

```go
func f3(t int) {
c3 := context.Background()
deadline := time.Now().Add(time.Duration(2*t) * time.Second)
c3, cancel := context.WithDeadline(c3, deadline)
defer cancel()

go func() {
time.Sleep(4 * time.Second)
cancel()
}()

select {
case <-c3.Done():
fmt.Println("f3():", c3.Err())
return
case r := <-time.After(time.Duration(t) * time.Second):
fmt.Println("f3():", r)
}
return
}
```

上面的代码说明了 `context.WithDeadline()` 函数的使用,它需要两个参数:`Context` 变量和一个表示操作将要截止的时间。当期限到了,`cancel()` 函数自动调用。

`simpleContext.go` 的最后一段代码如下:

```go
func main() {
if len(os.Args) != 2 {
fmt.Println("Need a delay!")
return
}

delay, err := strconv.Atoi(os.Args[1])
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Delay:", delay)

f1(delay)
f2(delay)
f3(delay)
}
```

执行 `simpleContext.go` 产生如下输出:

```shell
$go run simpleContext.go 4
Delay: 4
f1(): 2018-02-13 23:30:00.271587 +0200 EET m=+4.003435078
f2(): 2018-02-13 23:30:00.272678 +0200 EET m=+8.004706996
f3(): 2018-02-13 23:30:00.273738 +0200 EET m=+12.005937567
$go run simpleContext.go 10
Delay: 10
f1(): context canceled
f2(): context canceled
f3(): context canceled
```

输出较长的行是 `time.After()` 函数调用的返回值。它们代表程序正常操作。意味着如果该程序执行超时就会立刻被取消。

这与使用 `context` 包一样简单,因为介绍的代码没有对 `Context` 接口做任何重要的工作。不过,下节的 Go 代码将介绍一个更真实的例子。
6 changes: 3 additions & 3 deletions eBook/chapter13/13.6.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ func handleConnection(c net.Conn) {
c.Write([]byte(netData))
}
case "ADD":
n := myElement(tokens[2], tokens[3], tokens[4])
n := myElement{tokens[2], tokens[3], tokens[4]}
if !ADD(tokens[1], n) {
netData := "Add operation failed!\n"
c.Write([]byte(netData))
Expand Down Expand Up @@ -146,7 +146,7 @@ func save() error {
}
defer saveTo.Close()

encode := gob.NewEncoder(saveTo)
encoder := gob.NewEncoder(saveTo)
err = encoder.Encode(DATA)
if err != nil {
fmt.Println("Cannot save to", DATAFILE)
Expand Down Expand Up @@ -291,4 +291,4 @@ key: 4 value: {-1 -2 -3}
STOP
```

`kvTCP.go` 文件是一个使用 goroutines 的并发应用,它能够同时服务多个 TCP 客户端。然而,所有的 TCP 客户端共享相同的数据!
`kvTCP.go` 文件是一个使用 goroutines 的并发应用,它能够同时服务多个 TCP 客户端。然而,所有的 TCP 客户端共享相同的数据!
87 changes: 87 additions & 0 deletions eBook/examples/chapter10/simpleContext.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package main

import (
"context"
"fmt"
"os"
"strconv"
"time"
)

func f1(t int) {
c1 := context.Background()
c1, cancel := context.WithCancel(c1)
defer cancel()

go func() {
time.Sleep(4 * time.Second)
cancel()
}()
select {
case <-c1.Done():
fmt.Println("f1():", c1.Err())
return
case r := <-time.After(time.Duration(t) * time.Second):
fmt.Println("f1():", r)
}
return
}

func f2(t int) {
c2 := context.Background()
c2, cancel := context.WithTimeout(c2, time.Duration(t)*time.Second)
defer cancel()

go func() {
time.Sleep(4 * time.Second)
cancel()
}()

select {
case <-c2.Done():
fmt.Println("f2():", c2.Err())
return
case r := <-time.After(time.Duration(t) * time.Second):
fmt.Println("f2():", r)
}
return
}

func f3(t int) {
c3 := context.Background()
deadline := time.Now().Add(time.Duration(2*t) * time.Second)
c3, cancel := context.WithDeadline(c3, deadline)
defer cancel()

go func() {
time.Sleep(4 * time.Second)
cancel()
}()

select {
case <-c3.Done():
fmt.Println("f3():", c3.Err())
return
case r := <-time.After(time.Duration(t) * time.Second):
fmt.Println("f3():", r)
}
return
}

func main() {
if len(os.Args) != 2 {
fmt.Println("Need a delay!")
return
}

delay, err := strconv.Atoi(os.Args[1])
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Delay:", delay)

f1(delay)
f2(delay)
f3(delay)
}

0 comments on commit 0f1bf6f

Please sign in to comment.