> For the complete documentation index, see [llms.txt](https://wskdsgcf.gitbook.io/mastering-go-zh-cn/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wskdsgcf.gitbook.io/mastering-go-zh-cn/10.0/10.3/10.3.2.md).

# 10.3.2 方式2

第二个技巧的源码保存在 `timeOut2.go` 中，并分为五部分来介绍。这次，超时时间作为命令行参数提供给程序。

`timeOut2.go` 的第一部分如下：

```go
package main

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

`timeOut2.go` 的第二段代码如下：

```go
func timeout(w *sync.WaitGroup, t time.Duration) bool {
    temp := make(chan int)
    go func() {
        time.Sleep(5 * time.Second)
        defer close(temp)
        w.Wait()
    }()

    select {
        case <-temp:
            return false
        case <-time.After(t):
            return true
    }
}
```

上面的代码里，`time.After()` 调用使用的时间周期是 `timeout()` 函数的一个参数，就是说它可变。再次，`select` 块实现了超时逻辑。另外，`w.Wait()` 调用使 `timeout()` 函数无期限的等待一个匹配的 `sync.Done()` 函数来结束。当 `w.Wait()` 调用返回，`select` 表达式的第一个分支就会执行。

`timeOut2.go` 的第三段代码如下：

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

    var w sync.WaitGroup
    w.Add(1)
    t, err := strconv.Atoi(arguments[1])
    if err != nil {
        fmt.Println(err)
        return
    }
```

`timeOut2.go` 的第四部分如下：

```go
    duration := time.Duration(int32(t)) * time.Millisecond
    fmt.Printf("Timeout period is %s\n", duration)

    if timeout(&w, duration) {
        fmt.Println("Timed out!")
    } else {
        fmt.Println("OK!")
    }
```

`time.Duration()` 函数转换一个整数值给一个之后使用到的 `time.Duration` 变量。

`timeOut2.go` 的其余代码如下：

```go
    w.Done()
    if timeout(&w, duration) {
        fmt.Println("Timed out!")
    } else {
        fmt.Println("OK!")
    }
}
```

一旦 `w.Done()` 调用执行，之前的 `timeout()` 函数将返回。然而第二次调用 `timeout()` 没有要等待的 `sync.Done()` 语句。

执行 `timeOut2.go` 产生如下输出：

```
$go run timeOut2.go 10000
Timeout period is 10s
Timed out!
OK!
```

执行 `timeOut2.go` 的超时周期比匿名 goroutine 的 `time.Sleep(5 * time.Second)` 长。然而，没有必要的 `w.Done()` 调用，匿名 goroutine 不能返回因为 `time.After()` 调用先结束了，所以第一个 `if` 表达式的 `timeout()` 函数返回 `true`。第二个 `if` 表达式，匿名函数不必等待，因为 `time.Sleep(5 * time.Second)` 将先于 `time.After(t)` 结束，所以 `timeout()` 函数返回 `false`:

```
$go run timeOut2.go 100
Timeout period is 100ms
Timed out!
Timed out!
```

然而，第二次执行，这个超时周期太短了，所以这两个 `timeout()` 的执行都没有足够的时间完成，因此都超时了。

所以，当定义一个超时周期时，确保您选择了一个恰当的值，否则您的结果可能不是您期望的那样。
