Go语言:调度间隔用哪个好?time.Sleep v.s. time.After

ChaoCai · · 4543 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

Go编程中循环调度任务的执行间隔我们通常采用 time.Sleep或time.After来实现。 写法1: ``` go func(ctx context.Context) { for { select { case <-ctx.Done(): fmt.Println("Cancelled", time.Now()) wg.Done() return default: time.Sleep(time.Second * 10) fmt.Println("Invoked", time.Now()) } } }(ctx) ``` 写法2: ``` go func(ctx context.Context) { for { select { case <-ctx.Done(): fmt.Println("Cancelled", time.Now()) wg.Done() return case <-time.After(time.Second * 10): fmt.Println("Invoked", time.Now()) } } }(ctx) ``` 这两种方式一样吗?那种方式更好呢? 以上两种写法,都可以满足需求。那么是否这两种写法是等效的呢? 当然不是,为了更好的说明它们的差别,请运行下面的这两段测试代码: ``` var wg sync.WaitGroup func cancelTaskAfter(interval time.Duration, cancel context.CancelFunc) { go func(cancel context.CancelFunc) { time.Sleep(interval) fmt.Println("Cancell task", time.Now()) cancel() wg.Done() }(cancel) } func TestTimerTask1(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) wg.Add(1) cancelTaskAfter(time.Second*25, cancel) wg.Add(1) go func(ctx context.Context) { for { select { case <-ctx.Done(): fmt.Println("Cancelled", time.Now()) wg.Done() return default: time.Sleep(time.Second * 10) fmt.Println("Invoked", time.Now()) } } }(ctx) wg.Wait() } func TestTimerTask2(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) wg.Add(1) cancelTaskAfter(time.Second*25, cancel) wg.Add(1) go func(ctx context.Context) { for { select { case <-ctx.Done(): fmt.Println("Cancelled", time.Now()) wg.Done() return case <-time.After(time.Second * 10): fmt.Println("Invoked", time.Now()) } } }(ctx) wg.Wait() } ``` 也许看到这,你大概已经知道这两种写法的不同了。 以下是输出结果 ``` === RUN TestTimerTask1 Invoked 2019-09-22 17:12:07.055392 +0800 CST m=+35.007752073 Invoked 2019-09-22 17:12:17.057073 +0800 CST m=+45.009375450 Cancell task 2019-09-22 17:12:22.051311 +0800 CST m=+50.003583680 Invoked 2019-09-22 17:12:27.060766 +0800 CST m=+55.013010382 Cancelled 2019-09-22 17:12:27.060804 +0800 CST m=+55.013048342 --- PASS: TestTimerTask1 (30.01s) ``` ``` === RUN TestTimerTask2 Invoked 2019-09-22 17:11:42.049262 +0800 CST m=+10.001766181 Invoked 2019-09-22 17:11:52.053054 +0800 CST m=+20.005500478 Cancell task 2019-09-22 17:11:57.050145 +0800 CST m=+25.002563035 Cancelled 2019-09-22 17:11:57.05041 +0800 CST m=+25.002827648 --- PASS: TestTimerTask2 (25.00s) ``` 采用Sleep来实现间隔的时候,如果cancel调用后任务协程正好处于sleep过程中,这时任务是无法被及时取消的。 小结 如果你需要任务能够被及时的取消,那你应该采用time.After来控制调用的间隔。 更多知识,欢迎订阅学习 《Go 语言入门到实战》 ![IMG_4490.JPG](https://static.studygolang.com/190922/4f8d7857eac23e3f680ab93e640292d3.JPG)

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

4543 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传