基於time的ticker封裝的定時器

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

本人最近新學GO,為了能夠深入瞭解GO,於是計劃使用GO實現一個高並發、IO密集操作的ETL程式。閑話不多說,今天完成了ETL中最基本的要素,定時器。

首先瞭解到GO中的time.Ticker不適用到高精度的定時器,因為ETL任務周期設定是1分鐘,故此處無需高精度的定時器(對於高精度的定時器,在網上看到有人使用時間輪實現timewheel,我也看了實現,並且也按其思路實現了一版,此處略去)。

封閉的類型:TimeTicker,使用範例程式碼如下:

func main() {
    var tt *timeticker.TimeTicker = timeticker.New(3*time.Second)  //建立一個3秒鐘執行一次任務的定時器
    e := tt.AddJob(job, true)    //向此定時器中增加一個任務,返回此任務的指標(此指標在後續刪除任務時使用)
    go tt.Start()    //開一個協程,讓此定時器單獨去執行任務
    time.Sleep(6*time.Second)

    //主線程睡6秒後再加一個任務到定時器的任務隊列,之後每周期會執行兩個任務
    tt.AddJob(test_job, true) 
    time.Sleep(10*time.Second)
    tt.RemoveTask(e)  //主線程睡10秒後,清除掉第一個job,任務隊列就只剩一個任務了
    time.Sleep(10*time.Second)
    tt.Stop()    //停止此定時器,此後會發現定時器不再執行
    time.Sleep(5*time.Second)    //睡五秒後退出
}

func test_job(...interface{}) {
    fmt.Print("test_job is call ...")
    fmt.Println(time.Now())
}
func job(...interface{}) {
    fmt.Print("job is call ...")
    fmt.Println(time.Now())
}

程式執行結果:

job is call ...2018-04-11 22:21:42.0179853 +0800 CST m=+0.005000301
job is call ...2018-04-11 22:21:45.0181569 +0800 CST m=+3.005171901
test_job is call ...2018-04-11 22:21:48.0183285 +0800 CST m=+6.005343501
job is call ...2018-04-11 22:21:48.0183285 +0800 CST m=+6.005343501
job is call ...2018-04-11 22:21:51.0185001 +0800 CST m=+9.005515101
test_job is call ...2018-04-11 22:21:51.0185001 +0800 CST m=+9.005515101
test_job is call ...2018-04-11 22:21:54.0186717 +0800 CST m=+12.005686701
job is call ...2018-04-11 22:21:54.0186717 +0800 CST m=+12.005686701
test_job is call ...2018-04-11 22:21:57.0188433 +0800 CST m=+15.005858301
job is call ...2018-04-11 22:21:57.0188433 +0800 CST m=+15.005858301
test_job is call ...2018-04-11 22:22:00.0180149 +0800 CST m=+18.005029901
test_job is call ...2018-04-11 22:22:03.0181865 +0800 CST m=+21.005201501
test_job is call ...2018-04-11 22:22:06.0183581 +0800 CST m=+24.005373101
 

從上述運行結果可以看出,此定時器到秒級還算精準,但時間精度再小就會有偏差了,這就是開頭說的不適用於高精度的定時器。上面的使用範例程式碼中的注釋已經說明的很清楚了,下面獻上封裝的定時器的代碼(特意加了部分注釋,方便需要的同學理解):

//************************code begin********************************

package timeticker

import (
    "fmt"
    "time"
    //"reflect"
    "container/list" 
)

type Task struct {
    interval     time.Duration        //當前任務延遲多長時間後執行
    job            func(...interface{})                //回呼函數(任務的具體執行方法)
    isCycle        bool            //任務是否只執行一次(true,執行一次後就會從執行隊列中清除掉
}

//定時器類型
type TimeTicker struct {
    interval     time.Duration        //定時器間隔多長時間執行一次
    ticker       *time.Ticker
    taskQueue    *list.List
    closeChan    chan bool        //向此通道中輸入true值,則會關閉此定時器
}

func New(interval time.Duration) *TimeTicker {
    if interval <= 0 {
        return nil
    }
    tt := &TimeTicker {
        interval:        interval,
        taskQueue:     list.New(),
        closeChan:      make(chan bool),
    }
    return tt
}

//向定時器的任務隊列中增加任務
//參數:task--增加的任務
//            isCycle--是否迴圈執行, true,每次執行完後依然保留在任務隊列中,false,執行完一次後就會被清除掉
//考慮增加一組任務的方法
func (tt *TimeTicker) AddTask(task Task, isCycle bool) {
    tt.taskQueue.PushBack(task)
}

func (tt *TimeTicker) AddTaskList(task Task, isCycle bool) {
    
}

//增加一個執行任務    參數:job--具體執行的函數      
//         isCycle--是否迴圈執行,true:迴圈執行,每個周期均執行一次,    false:只執行一次,然後從任務隊列中清除
func (tt *TimeTicker) AddJob(job func(...interface{}), isCycle bool) *list.Element {
    task := &Task {
        interval:    0,
        job:        job,
        isCycle:    isCycle,
    }
    e := tt.taskQueue.PushBack(task)
    return e
}

func (tt *TimeTicker) RemoveTask(e *list.Element) {
    tt.taskQueue.Remove(e)
    //fmt.Println(tt.taskQueue.Len())
}

func (tt *TimeTicker) Stop() {
    tt.ticker.Stop()
    tt.closeChan <- true
}
//啟動定時器,如果不傳參數表示立刻啟動,否則delay時間後啟動
func (tt *TimeTicker) Start(delay ...time.Duration) {
    
    if len(delay) > 0 {        //延遲delay[0]時間後啟動
        
    } else {            //立刻啟動
        tt.ticker = time.NewTicker(tt.interval)    
        tt.executeTaskQueue()
        for {
            select {
                case <- tt.ticker.C:
                    tt.executeTaskQueue()
                case <- tt.closeChan:
                    return
            }
        }
        
    }
}

func (tt *TimeTicker) executeTaskQueue() {
    
    if tt.taskQueue.Len() == 0 {
        fmt.Println("任務隊列為空白,沒有可執行檔任務......")
        return
    }
    
    for iter := tt.taskQueue.Front(); iter != nil; iter = iter.Next() {
        t := iter.Value
        task,ok := t.(*Task)
        if ok {
            go task.job()
            if !task.isCycle {
                iter_tmp := iter
                iter = iter.Prev()
                tt.taskQueue.Remove(iter_tmp)
                if iter == nil {
                    return
                }
            }
        }
    }
}

//設定逾時機制,當設定此函數後,定時器會從任務啟動時間開始計時,
//如果達到此時間後,任務隊列中的任務達仍然未被執行完,則中止中剩餘的任務,並發出警告
func (tt *TimeTicker) SetTimeOut(setTime time.Duration) {
    
}

//************************code end********************************

大部分函數前都加了很詳細的注釋,方便大家閱讀。還有一些設計好的函數並未實現,但主體功能都已經出來了,並不影響使用,且最主要的原因是ETL中沒有這些(未實現的函數)功能的需求,就偷個賴了。

 

 

338 次點擊  ∙  1 贊  
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.