forked from hantmac/Mastering_Go_ZH_CN
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch '10.7.1' of github.com:themoonbear/Mastering_Go_ZH_CN in…
…to 10.7.1
- Loading branch information
Showing
4 changed files
with
246 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 代码将介绍一个更真实的例子。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |