Time Package Notes

Go中的time是一个日常开发中经常用到的package。这里总结一些常用的方式。

获取当前的时间

1
2
3
4
5
6
7
import (
	"time"
	"fmt"
)

now := time.Now()
fmt.Println("current time:", now)

time.Now() 返回的是一个Time对象。上面的代码示例的输出结果为:

current time: 2024-11-06 18:10:25.099407 +0800 CST m=+0.000157959

其中 +0800表示当前的时间是UTC+8的时间,CST是China Standard Time,中国标准时间。

这样的时间格式不一定是我们需要的,所以我们可以用Time对象的Format方法对它进行格式化处理,

1
fmt.Println(now.Format("2006-01-02 15:04:05"))

睡眠 time.Sleep

time.Sleep() 用于让当前的goroutine暂停执行,让协程调度器执行其它的goroutine。

1
2
// 睡眠10秒
time.Sleep(10 * time.Second)

定时器 time.NewTimer()

Timer是time包中有一个很重要的type,它会以当前时间为起点,在值得的时间周期到后,触发Timer,然后Timer会给自己的一个Channel中发送一个时间值。

time.NewTimer()就是返回一个Timer对象,我们可以读取这个对象的C通道,实现time.Sleep类似的效果。

1
2
3
4
timer := time.NewTimer(10 * time.Second)
defer timer.Stop()

<- timer.C  // 在十秒达到前,这里会被阻塞,实现了sleep的效果。

Timer有两个主要的应用场景:

  • 超时检测
  • 延迟执行

超时检测

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
func makeNetworkRequest() (string, error) {
    // 模拟网络请求,这里假设需要 10 秒才能完成
    time.Sleep(10 * time.Second)
    return "Response from network", nil
}

func main() {
    // 创建一个 5 秒超时的 Timer
    timer := time.NewTimer(5 * time.Second)
    defer timer.Stop()

    // 并发执行网络请求和超时检测
    done := make(chan string)
    go func() {
        resp, err := makeNetworkRequest()
        if err != nil {
            done <- err.Error()
        } else {
            done <- resp
        }
    }()

    select {
    case res := <-done:
        fmt.Println("Request completed:", res)
    case <-timer.C:
        fmt.Println("Request timed out")
    }
}

超时控制,除了使用time.NewTimer()的方式,还可以使用time.After(),

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
func (s *Server) performScan(path string) (string, error) {
	c := clamd.NewClamd(s.ClamAVDaemonHost)

	responseCh := make(chan chan *clamd.ScanResult)
	errCh := make(chan error)
	go func(responseCh chan chan *clamd.ScanResult, errCh chan error) {
		response, err := c.ScanFile(path)
		if err != nil {
			errCh <- err
			return
		}

		responseCh <- response
	}(responseCh, errCh)

	select {
	case err := <-errCh:
		return "", err
	case response := <-responseCh:
		st := <-response
		return st.Status, nil
	case <-time.After(time.Second * 60):
		return "", errors.New("clamav scan timeout")
	}
}

上面的代码片段是从 https://sourcegraph.com/github.com/dutchcoders/transfer.sh/-/blob/server/clamav.go?L98-100 获取到的,可以看到,当扫描一个路径超过60秒后,就会超时返回错误。

延迟执行

延迟执行是指让干某件事先延迟一段时间,再执行,例如下面的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
func doSomethingLater() {
    fmt.Println("Doing something later")
}

func main() {

    timer := time.NewTimer(5 * time.Second)
    defer timer.Stop()

    <-timer.C // // 延迟 5 秒后执行 doSomethingLater 函数
    doSomethingLater()
}

在上面的延迟场景中,用time.Sleep(5 * time.Second)也可以实现延迟效果。

周期执行 time.NewTicker()

time.NewTicker()可以周期性地重复执行某个任务。

比如,我想让程序每秒打印一次统计数据,则可以使用Ticker

1
2
3
4
5
6
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()

for range ticker.C {
	// do something
}