2017-05-10 111 views
1

我有一個程序,它定期檢查一個外部郵箱的消息,其中有一個用戶視圖允許他們查看消息並終止程序。如何使用time.After()而不是time.Sleep()獲取可中斷暫停

剝離到最小功能,它看起來像這樣

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" 
} 

您可以run this in the Go Playground

輸出是

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 

注意

  • "23:00:15 Fetching"是我沒想到的。
  • 程序在23:00:15退出,而不是在23:00:12,因爲它正在睡覺。

後者會在我的程序中出現問題,因爲它在檢查消息之間使用了10分鐘的睡眠時間。延遲退出這段時間會使程序看起來很沒有反應。

this answer我瞭解到,您可以使用time.After()創建可以中斷的循環延遲。

我應該如何最好地將它應用到我的程序中?

回答

3

「23:00:15提取」是我沒想到的。

這並不奇怪,這是預期的工作。從Spec: Select statements報價:

在幾個步驟的「選擇」的聲明收益的執行:

  1. 對於在聲明中所有的情況下,渠道操作數接收操作和通道和右手工在輸入「select」語句後,按照源順序對發送語句的表達式進行一次精確評估。

[...]

所以select語句計算通信操作它決定在哪個分支繼續/執行之前。

這意味着

case data <- fetch(): 

fetch()將被調用,即使發送的data不會是可能的,即使從quit接收可以立即進行。

既然你在case分支之一的睡眠,這不要緊,quit準備好從,檢查(和可選決定去該分支)已收到等到time.Sleep() - 和全case分支完成。

所以通信操作應該是來自time.After()返回的通道的接收操作,並且只在本案例分支的主體中調用fetch()

你可以把它像這樣工作的:

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() 
    } 
} 

而且現在的輸出(嘗試在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 
相關問題