doukuipai8544 2017-05-10 11:36
浏览 208
已采纳

如何使用time.After()而不是time.Sleep()获得可中断的暂停

I have a program which periodically checks an external mailbox for messages and which has a user view which allows them to view messages and to terminate the program.

Stripped to minimal features it looks like this

package main

import (
    "log"
    "time"
)

func main() {
    log.Println("Hello, playground")

    quit := make(chan bool)
    data := make(chan string)

    go func() {
        for {
            select {
            case <-quit:
                log.Println("Quitting")
                close(data)
                return
            case data <- fetch():
                // Wait until e.g. exactly 0,10,20,30,40 or 50 mins past the hour
                interval := time.Second * 5
                now := time.Now()
                time.Sleep(now.Truncate(interval).Add(interval).Sub(now))
            }
        }
    }()

    go func() {
        time.Sleep(12 * time.Second) // actually user presses a "quit" button
        quit <- true
    }()

loop:
    for {
        select {
        case info, ok := <-data:
            if !ok {
                break loop
            }
            log.Println("Fetched", info)
        }
    }

    log.Println("Goodbye, playground")
}

func fetch() string {
    log.Println("Fetching")
    return "message"
}

You can run this in the Go Playground

Output is

2009/11/10 23:00:00 Hello, playground
2009/11/10 23:00:00 Fetching
2009/11/10 23:00:00 Fetched message
2009/11/10 23:00:05 Fetching
2009/11/10 23:00:05 Fetched message
2009/11/10 23:00:10 Fetching
2009/11/10 23:00:10 Fetched message
2009/11/10 23:00:15 Fetching
2009/11/10 23:00:15 Quitting
2009/11/10 23:00:15 Goodbye, playground

Notice that

  • "23:00:15 Fetching" is something I didn't expect.
  • The program quits at 23:00:15, not at 23:00:12 because it's sleeping.

The latter would be a problem in my program because it uses a 10 minute sleep between checking for messages. Delaying a quit for that long would make the program seem pretty unresponsive.

From this answer I have learned that you can use time.After() to create a loop delay that can be interrupted.

How should I best apply that to my program?

  • 写回答

1条回答 默认 最新

  • duano3557 2017-05-10 11:58
    关注

    "23:00:15 Fetching" is something I didn't expect.

    This is not surprising, this is the intended working. Quoting from Spec: Select statements:

    Execution of a "select" statement proceeds in several steps:

    1. For all the cases in the statement, the channel operands of receive operations and the channel and right-hand-side expressions of send statements are evaluated exactly once, in source order, upon entering the "select" statement.

    [...]

    So the select statement evaluates the communication operations before it decides on which branch to proceed / execute.

    This means that

    case data <- fetch():
    

    fetch() will be called, even if sending on data would not be possible and even if receiving from quit can proceed immediately.

    Since you have the sleep in one of the case branches, it doesn't matter that quit becomes ready to receive from, checking that (and optionally deciding to go on that branch) has to wait until time.Sleep() –and the whole case branch– completes.

    So the communication operation should be a receive operation from the channel returned by time.After(), and only call fetch() in the body of this case branch.

    You can make it work like this:

    for {
        // Wait until e.g. exactly 0,10,20,30,40 or 50 mins past the hour
        interval := time.Second * 5
        now := time.Now()
        delay := now.Truncate(interval).Add(interval).Sub(now)
    
        select {
        case <-quit:
            log.Println("Quitting")
            close(data)
            return
        case <-time.After(delay):
            data <- fetch()
        }
    }
    

    And now the output (try it on the Go Playground):

    2009/11/10 23:00:00 Hello, playground
    2009/11/10 23:00:05 Fetching
    2009/11/10 23:00:05 Fetched message
    2009/11/10 23:00:10 Fetching
    2009/11/10 23:00:10 Fetched message
    2009/11/10 23:00:12 Quitting
    2009/11/10 23:00:12 Goodbye, playground
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 关于#hadoop#的问题
  • ¥15 (标签-Python|关键词-socket)
  • ¥15 keil里为什么main.c定义的函数在it.c调用不了
  • ¥50 切换TabTip键盘的输入法
  • ¥15 可否在不同线程中调用封装数据库操作的类
  • ¥15 微带串馈天线阵列每个阵元宽度计算
  • ¥15 keil的map文件中Image component sizes各项意思
  • ¥20 求个正点原子stm32f407开发版的贪吃蛇游戏
  • ¥15 划分vlan后,链路不通了?
  • ¥20 求各位懂行的人,注册表能不能看到usb使用得具体信息,干了什么,传输了什么数据