激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

腳本之家,腳本語(yǔ)言編程技術(shù)及教程分享平臺(tái)!
分類導(dǎo)航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務(wù)器之家 - 腳本之家 - Golang - golang 定時(shí)任務(wù)方面time.Sleep和time.Tick的優(yōu)劣對(duì)比分析

golang 定時(shí)任務(wù)方面time.Sleep和time.Tick的優(yōu)劣對(duì)比分析

2021-06-18 01:00Star_CSU Golang

這篇文章主要介紹了golang 定時(shí)任務(wù)方面time.Sleep和time.Tick的優(yōu)劣對(duì)比分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧

golang 寫循環(huán)執(zhí)行的定時(shí)任務(wù),常見的有以下三種實(shí)現(xiàn)方式

 

1、time.Sleep方法:

?
1
2
3
4
for {
   time.Sleep(time.Second)
   fmt.Println("我在定時(shí)執(zhí)行任務(wù)")
}

2、time.Tick函數(shù):

?
1
2
3
4
5
6
7
t1:=time.Tick(3*time.Second)
for {
   select {
   case <-t1:
      fmt.Println("t1定時(shí)器")
   }
}

3、其中Tick定時(shí)任務(wù)

也可以先使用time.Ticker函數(shù)獲取Ticker結(jié)構(gòu)體,然后進(jìn)行阻塞監(jiān)聽信息,這種方式可以手動(dòng)選擇停止定時(shí)任務(wù),在停止任務(wù)時(shí),減少對(duì)內(nèi)存的浪費(fèi)。

?
1
2
3
4
5
6
7
8
t:=time.NewTicker(time.Second)
for {
   select {
   case <-t.C:
      fmt.Println("t1定時(shí)器")
      t.Stop()
   }
}

其中第二種和第三種可以歸為同一類

這三種定時(shí)器的實(shí)現(xiàn)原理

 

一般來說,你在使用執(zhí)行定時(shí)任務(wù)的時(shí)候,一般旁人會(huì)勸你不要使用time.Sleep完成定時(shí)任務(wù),但是為什么不能使用Sleep函數(shù)完成定時(shí)任務(wù)呢,它和Tick函數(shù)比,有什么劣勢(shì)呢?這就需要我們?nèi)ヌ接戦喿x一下源碼,分析一下它們之間的優(yōu)劣性。

首先,我們研究一下Tick函數(shù),func Tick(d Duration) <-chan Time

調(diào)用Tick函數(shù)會(huì)返回一個(gè)時(shí)間類型的channel,如果對(duì)channel稍微有些了解的話,我們首先會(huì)想到,既然是返回一個(gè)channel,在調(diào)用Tick方法的過程中,必然創(chuàng)建了goroutine,該Goroutine負(fù)責(zé)發(fā)送數(shù)據(jù),喚醒被阻塞的定時(shí)任務(wù)。我在閱讀源碼之后,確實(shí)發(fā)現(xiàn)函數(shù)中g(shù)o出去了一個(gè)協(xié)程,處理定時(shí)任務(wù)。

按照當(dāng)前的理解,使用一個(gè)tick,需要go出去一個(gè)協(xié)程,效率和對(duì)內(nèi)存空間的占用肯定不能比sleep函數(shù)強(qiáng)。我們需要繼續(xù)閱讀源碼才拿獲取到真理。

簡(jiǎn)單的調(diào)用過程我就不陳述了,我在這介紹一下核心結(jié)構(gòu)體和方法(刪除了部分判斷代碼,解釋我寫在表格中):

?
1
2
3
4
5
6
7
8
9
func (tb *timersBucket) addtimerLocked(t *timer) {
   t.i = len(tb.t)  //計(jì)算timersBucket中,當(dāng)前定時(shí)任務(wù)的長(zhǎng)度
   tb.t = append(tb.t, t)// 將當(dāng)前定時(shí)任務(wù)加入timersBucket
   siftupTimer(tb.t, t.i)  //維護(hù)一個(gè)timer結(jié)構(gòu)體的最小堆(四叉樹),排序關(guān)鍵字為執(zhí)行時(shí)間,即該定時(shí)任務(wù)下一次執(zhí)行的時(shí)間
   if !tb.created {
      tb.created = true
      go timerproc(tb)// 如果還沒有創(chuàng)建過管理定時(shí)任務(wù)的協(xié)程,則創(chuàng)建一個(gè),執(zhí)行通知管理timer的協(xié)程,最核心代碼
   }
}

timersBucket,顧名思義,時(shí)間任務(wù)桶,是外界不可見的全局變量。每當(dāng)有新的timer定時(shí)器任務(wù)時(shí),會(huì)將timer加入到timersBucket中的timer切片。timerBucket結(jié)構(gòu)體如下:

?
1
2
3
4
type timersBucket struct {
   lock         mutex //添加新定時(shí)任務(wù)時(shí)需要加鎖(沖突點(diǎn)在于維護(hù)堆)
   t            []*timer //timer切片,構(gòu)造方式為四叉樹最小堆
}

func timerproc(tb *timersBucket) 詳細(xì)介紹

 

可以稱之為定時(shí)任務(wù)處理器,所有的定時(shí)任務(wù)都會(huì)加入timersBucket,然后在該函數(shù)中等待被處理。

等待被處理的timer,根據(jù)when字段(任務(wù)執(zhí)行的時(shí)間,int類型,納秒級(jí)別)構(gòu)成一個(gè)最小堆,每次處理完成堆頂?shù)哪硞€(gè)timer時(shí),會(huì)給它的when字段加上定時(shí)任務(wù)循環(huán)間隔時(shí)間(即Tick(d Duration) 中的d參數(shù)),然后重新維護(hù)堆,保證when最小的timer在堆頂。當(dāng)堆中沒有可以處理的timer(有timer,但是還不到執(zhí)行時(shí)間),需要計(jì)算當(dāng)前時(shí)間和堆頂中timer的任務(wù)執(zhí)行時(shí)間差值delta,定時(shí)任務(wù)處理器沉睡delta段時(shí)間,等待被調(diào)度器喚醒。

核心代碼如下(注釋寫在每行代碼的后面,刪除一些判斷代碼以及不利于閱讀的非核心代碼):

?
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
func timerproc(tb *timersBucket) {
   for {
      lock(&tb.lock) //加鎖
      now := nanotime()  //當(dāng)前時(shí)間的納秒值
      delta := int64(-1)  //最近要執(zhí)行的timer和當(dāng)前時(shí)間的差值
      for {
         if len(tb.t) == 0 {
            delta = -1
            break
         }//當(dāng)前無可執(zhí)行timer,直接跳出該循環(huán)
         t := tb.t[0]
         delta = t.when - now //取when組小的的timer,計(jì)算于當(dāng)前時(shí)間的差值
         if delta > 0 {
            break
         }// delta大于0,說明還未到發(fā)送channel時(shí)間,需要跳出循環(huán)去睡眠delta時(shí)間
         if t.period > 0 {
            // leave in heap but adjust next time to fire
            t.when += t.period * (1 + -delta/t.period)// 計(jì)算該timer下次執(zhí)行任務(wù)的時(shí)間
            siftdownTimer(tb.t, 0) //調(diào)整堆
         } else {
            // remove from heap,如果沒有設(shè)定下次執(zhí)行時(shí)間,則將該timer從堆中移除(time.after和time.sleep函數(shù)即是只執(zhí)行一次定時(shí)任務(wù))
            last := len(tb.t) - 1
            if last > 0 {
               tb.t[0] = tb.t[last]
               tb.t[0].i = 0
            }
            tb.t[last] = nil
            tb.t = tb.t[:last]
            if last > 0 {
               siftdownTimer(tb.t, 0)
            }
            t.i = -1 // mark as removed
         }
         f := t.f
         arg := t.arg
         seq := t.seq
         unlock(&tb.lock)//解鎖
         f(arg, seq) //在channel中發(fā)送time結(jié)構(gòu)體,喚醒阻塞的協(xié)程
         lock(&tb.lock)
      }
      if delta < 0  {
         // No timers left - put goroutine to sleep.
         goparkunlock(&tb.lock, "timer goroutine (idle)", traceEvGoBlock, 1)
         continue
      }// delta小于0說明當(dāng)前無定時(shí)任務(wù),直接進(jìn)行阻塞進(jìn)行睡眠
      tb.sleeping = true
      tb.sleepUntil = now + delta
      unlock(&tb.lock)
      notetsleepg(&tb.waitnote, delta)  //睡眠delta時(shí)間,喚醒之后就可以執(zhí)行在堆頂?shù)亩〞r(shí)任務(wù)了
   }
}

至此,time.Tick函數(shù)涉及到的主要功能就講解結(jié)束了,總結(jié)一下就是啟動(dòng)定時(shí)任務(wù)時(shí),會(huì)創(chuàng)建一個(gè)唯一協(xié)程,處理timer,所有的timer都在該協(xié)程中處理。

然后,我們?cè)匍喿x一下sleep的源碼實(shí)現(xiàn),核心源碼如下:

?
1
2
3
4
5
6
7
8
//go:linkname timeSleep time.Sleep
func timeSleep(ns int64) {
   *t = timer{} //創(chuàng)建一個(gè)定時(shí)任務(wù)
   t.when = nanotime() + ns //計(jì)算定時(shí)任務(wù)的執(zhí)行時(shí)間點(diǎn)
   t.f = goroutineReady //執(zhí)行方法
   tb.addtimerLocked(t)  //加入timer堆,并在timer定時(shí)任務(wù)執(zhí)行協(xié)程中等待被執(zhí)行
   goparkunlock(&tb.lock, "sleep", traceEvGoSleep, 2) //睡眠,等待定時(shí)任務(wù)協(xié)程通知喚醒
}

讀了sleep的核心代碼之后,是不是突然發(fā)現(xiàn)和Tick函數(shù)的內(nèi)容很類似,都創(chuàng)建了timer,并加入了定時(shí)任務(wù)處理協(xié)程。神奇之處就在于,實(shí)際上這兩個(gè)函數(shù)產(chǎn)生的timer都放入了同一個(gè)timer堆,都在定時(shí)任務(wù)處理協(xié)程中等待被處理。

優(yōu)劣性對(duì)比,使用建議

 

現(xiàn)在我們知道了,Tick,Sleep,包括time.After函數(shù),都使用的timer結(jié)構(gòu)體,都會(huì)被放在同一個(gè)協(xié)程中統(tǒng)一處理,這樣看起來使用Tick,Sleep并沒有什么區(qū)別。

實(shí)際上是有區(qū)別的,Sleep是使用睡眠完成定時(shí)任務(wù),需要被調(diào)度喚醒。Tick函數(shù)是使用channel阻塞當(dāng)前協(xié)程,完成定時(shí)任務(wù)的執(zhí)行。當(dāng)前并不清楚golang 阻塞和睡眠對(duì)資源的消耗會(huì)有什么區(qū)別,這方面不能給出建議。

但是使用channel阻塞協(xié)程完成定時(shí)任務(wù)比較靈活,可以結(jié)合select設(shè)置超時(shí)時(shí)間以及默認(rèn)執(zhí)行方法,而且可以設(shè)置timer的主動(dòng)關(guān)閉,以及不需要每次都生成一個(gè)timer(這方面節(jié)省系統(tǒng)內(nèi)存,垃圾收回也需要時(shí)間)。

所以,建議使用time.Tick完成定時(shí)任務(wù)。

補(bǔ)充:Golang 定時(shí)器timer和ticker

兩種類型的定時(shí)器:ticker和timer。兩者有什么區(qū)別呢?請(qǐng)看如下代碼:

ticker

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main
import (
        "fmt"
        "time"
)
func main() {
        d := time.Duration(time.Second*2)
        t := time.NewTicker(d)
        defer t.Stop()
        for {
                <- t.C
                fmt.Println("timeout...")
        }
}

output:

timeout…

timeout…

timeout…

解析

ticker只要定義完成,從此刻開始計(jì)時(shí),不需要任何其他的操作,每隔固定時(shí)間都會(huì)觸發(fā)。

timer

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main
import (
        "fmt"
        "time"
)
func main() {
        d := time.Duration(time.Second*2)
        t := time.NewTimer(d)
        defer t.Stop()
        for {
                <- t.C
                fmt.Println("timeout...")
  // need reset
  t.Reset(time.Second*2)
        }
}

output:

timeout…

timeout…

timeout…

解析

使用timer定時(shí)器,超時(shí)后需要重置,才能繼續(xù)觸發(fā)。

ticker 例子展示

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main
import (
        "fmt"
        "time"
)
func main() {
        t := time.NewTicker(3*time.Second)
        defer t.Stop()
        fmt.Println(time.Now())
        time.Sleep(4*time.Second)
        for {
                select {
                case <-t.C:
                        fmt.Println(time.Now())
                }
        }
}

output:

2018-04-02 19:08:22.2797 +0800 CST

2018-04-02 19:08:26.3087 +0800 CST

2018-04-02 19:08:28.2797 +0800 CST

2018-04-02 19:08:31.2797 +0800 CST

2018-04-02 19:08:34.2797 +0800 CST

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持服務(wù)器之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。

原文鏈接:https://blog.csdn.net/Star_CSU/article/details/86650684

延伸 · 閱讀

精彩推薦
  • GolangGolang通脈之?dāng)?shù)據(jù)類型詳情

    Golang通脈之?dāng)?shù)據(jù)類型詳情

    這篇文章主要介紹了Golang通脈之?dāng)?shù)據(jù)類型,在編程語(yǔ)言中標(biāo)識(shí)符就是定義的具有某種意義的詞,比如變量名、常量名、函數(shù)名等等,Go語(yǔ)言中標(biāo)識(shí)符允許由...

    4272021-11-24
  • Golanggolang如何使用struct的tag屬性的詳細(xì)介紹

    golang如何使用struct的tag屬性的詳細(xì)介紹

    這篇文章主要介紹了golang如何使用struct的tag屬性的詳細(xì)介紹,從例子說起,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看...

    Go語(yǔ)言中文網(wǎng)11352020-05-21
  • Golanggolang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法

    golang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法

    今天小編就為大家分享一篇golang json.Marshal 特殊html字符被轉(zhuǎn)義的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧 ...

    李浩的life12792020-05-27
  • Golanggolang的httpserver優(yōu)雅重啟方法詳解

    golang的httpserver優(yōu)雅重啟方法詳解

    這篇文章主要給大家介紹了關(guān)于golang的httpserver優(yōu)雅重啟的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,...

    helight2992020-05-14
  • GolangGolang中Bit數(shù)組的實(shí)現(xiàn)方式

    Golang中Bit數(shù)組的實(shí)現(xiàn)方式

    這篇文章主要介紹了Golang中Bit數(shù)組的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧...

    天易獨(dú)尊11682021-06-09
  • Golanggolang 通過ssh代理連接mysql的操作

    golang 通過ssh代理連接mysql的操作

    這篇文章主要介紹了golang 通過ssh代理連接mysql的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧...

    a165861639710342021-03-08
  • Golanggo語(yǔ)言制作端口掃描器

    go語(yǔ)言制作端口掃描器

    本文給大家分享的是使用go語(yǔ)言編寫的TCP端口掃描器,可以選擇IP范圍,掃描的端口,以及多線程,有需要的小伙伴可以參考下。 ...

    腳本之家3642020-04-25
  • Golanggo日志系統(tǒng)logrus顯示文件和行號(hào)的操作

    go日志系統(tǒng)logrus顯示文件和行號(hào)的操作

    這篇文章主要介紹了go日志系統(tǒng)logrus顯示文件和行號(hào)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧...

    SmallQinYan12302021-02-02
主站蜘蛛池模板: 国产亚洲精品久久久久婷婷瑜伽 | 高清国产午夜精品久久久久久 | 一级黄色免费观看 | 色综合久久久久久 | 久久久久久久亚洲精品 | 日韩精品| 免费看成年人网站 | 亚洲精品一区国产精品丝瓜 | 国产成年人在线观看 | 亚洲一区中文字幕 | 欧美一级高潮 | 亚洲精品欧美 | 精品亚洲一区二区三区 | 欧美18—19sex性hd按摩 | 久久人添人人爽人人爽人人片av | 亚洲欧美在线视频免费 | 黄色特级| 鲁丝一区二区二区四区 | 一区二区三区日韩 | 羞羞网站在线观看入口免费 | 国产成人精品午夜视频' | 91高清观看 | 黄色一级片在线免费观看 | 久草在线综合 | 精品国产91久久久久久浪潮蜜月 | 亚洲第一成网站 | 护士xxxx | 欧美黄色看 | 久久久久久久久久综合 | 免费91在线 | 国产人妖一区二区 | 久久伊 | 欧美成人精品h版在线观看 国产一级淫片在线观看 | 亚洲综人网 | 成年人黄色片视频 | 999精品国产 | 国产一区二区三区在线观看视频 | 久色视频网站 | 国产正在播放 | 免费看操片 | 精品在线视频观看 |