校园春色亚洲色图_亚洲视频分类_中文字幕精品一区二区精品_麻豆一区区三区四区产品精品蜜桃

主頁 > 知識庫 > Go timer如何調(diào)度

Go timer如何調(diào)度

熱門標簽:揚州電銷外呼系統(tǒng)軟件 上海企業(yè)外呼系統(tǒng)排名 開通400電話申請流程 400手機電話免費辦理 如何利用高德地圖標注家 電腦外呼系統(tǒng)輻射大嗎 智能語音電銷的機器人 武漢百應(yīng)人工智能電銷機器人 百度地圖標注位置網(wǎng)站

本篇文章剖析下 Go 定時器的相關(guān)內(nèi)容。定時器不管是業(yè)務(wù)開發(fā),還是基礎(chǔ)架構(gòu)開發(fā),都是繞不過去的存在,由此可見定時器的重要程度。

我們不管用 NewTimer, timer.After,還是 timer.AfterFun 來初始化一個 timer, 這個 timer 最終都會加入到一個全局 timer 堆中,由 Go runtime 統(tǒng)一管理。

全局的 timer 堆也經(jīng)歷過三個階段的重要升級。

  • Go 1.9 版本之前,所有的計時器由全局唯一的四叉堆維護,協(xié)程間競爭激烈。
  • Go 1.10 - 1.13,全局使用 64 個四叉堆維護全部的計時器,沒有本質(zhì)解決 1.9 版本之前的問題
  • Go 1.14 版本之后,每個 P 單獨維護一個四叉堆。

Go 1.14 以后的 timer 性能得到了質(zhì)的飛升,不過伴隨而來的是 timer 成了 Go 里面最復(fù)雜、最難梳理的數(shù)據(jù)結(jié)構(gòu)。本文不會詳細分析每一個細節(jié),我們從大體來了解 Go timer 的工作原理。

1. 使用場景

Go timer 在我們代碼中會經(jīng)常遇到。

場景1:RPC 調(diào)用的防超時處理(下面代碼節(jié)選 dubbogo)

func (c *Client) Request(request *remoting.Request, timeout time.Duration, response *remoting.PendingResponse) error {
    _, session, err := c.selectSession(c.addr)
    // .. 省略
    if totalLen, sendLen, err = c.transfer(session, request, timeout); err != nil {
        if sendLen != 0  totalLen != sendLen {
          // .. 省略
        }
        return perrors.WithStack(err)
    }

    // .. 省略
    select {
    case -getty.GetTimeWheel().After(timeout):
        return perrors.WithStack(errClientReadTimeout)
    case -response.Done:
        err = response.Err
    }
    return perrors.WithStack(err)
}

場景2:Context 的超時處理

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
    defer cancel()
    go doSomething()
    
    select {
    case -ctx.Done():
        fmt.Println("main", ctx.Err())
    }
}

2. 圖解源碼

2.1 四叉堆原理

timer 的全局堆是一個四叉堆,特別是 Go 1.14 之后每個 P 都會維護著一個四叉堆,減少了 Goroutine 之間的并發(fā)問題,提升了 timer 了性能。

四叉堆其實就是四叉樹,Go timer 是如何維護四叉堆的呢?

  • Go runtime 調(diào)度 timer 時,觸發(fā)時間更早的 timer,要減少其查詢次數(shù),盡快被觸發(fā)。所以四叉樹的父節(jié)點的觸發(fā)時間是一定小于子節(jié)點的。
  • 四叉樹顧名思義最多有四個子節(jié)點,為了兼顧四叉樹插、刪除、重排速度,所以四個兄弟節(jié)點間并不要求其按觸發(fā)早晚排序。

這里用兩張動圖簡單演示下 timer 的插入和刪除

把 timer 插入堆

把 timer 從堆中刪除

2.2 timer 是如何被調(diào)度的?

調(diào)用 NewTimer,timer.After, timer.AfterFunc 生產(chǎn) timer, 加入對應(yīng)的 P 的堆上。

調(diào)用 timer.Stop, timer.Reset 改變對應(yīng)的 timer 的狀態(tài)。

GMP 在調(diào)度周期內(nèi)中會調(diào)用 checkTimers ,遍歷該 P 的 timer 堆上的元素,根據(jù)對應(yīng) timer 的狀態(tài)執(zhí)行真的操作。

2.3 timer 是如何加入到 timer 堆上的?

把 timer 加入調(diào)度總共有下面幾種方式:

  • 通過 NewTimer, time.After, timer.AfterFunc 初始化 timer 后,相關(guān) timer 就會被放入到對應(yīng) p 的 timer 堆上。
  • timer 已經(jīng)被標記為 timerRemoved,調(diào)用了 timer.Reset(d),這個 timer 也會重新被加入到 p 的 timer 堆上
  • timer 還沒到需要被執(zhí)行的時間,被調(diào)用了 timer.Reset(d),這個 timer 會被 GMP 調(diào)度探測到,先將該 timer 從 timer 堆上刪除,然后重新加入到 timer 堆上
  • STW 時,runtime 會釋放不再使用的 p 的資源,p.destroy()->timer.moveTimers,將不再被使用的 p 的 timers 上有效的 timer(狀態(tài)是:timerWaiting,timerModifiedEarlier,timerModifiedLater) 都重新加入到一個新的 p 的 timer 上

2.4 Reset 時 timer 是如何被操作的?

Reset 的目的是把 timer 重新加入到 timer 堆中,重新等待被觸發(fā)。不過分為兩種情況:

  • 被標記為 timerRemoved 的 timer,這種 timer 是已經(jīng)從 timer 堆上刪除了,但會重新設(shè)置被觸發(fā)時間,加入到 timer 堆中
  • 等待被觸發(fā)的 timer,在 Reset 函數(shù)中只會修改其觸發(fā)時間和狀態(tài)(timerModifiedEarlier或timerModifiedLater)。這個被修改狀態(tài)的 timer 也同樣會被重新加入到 timer堆上,不過是由 GMP 觸發(fā)的,由 checkTimers 調(diào)用 adjusttimers 或者 runtimer 來執(zhí)行的。

2.5 Stop 時 timer 是如何被操作的?

time.Stop 為了讓 timer 停止,不再被觸發(fā),也就是從 timer 堆上刪除。不過 timer.Stop 并不會真正的從 p 的 timer 堆上刪除 timer,只會將 timer 的狀態(tài)修改為 timerDeleted。然后等待 GMP 觸發(fā)的 adjusttimers 或者 runtimer 來執(zhí)行。

真正刪除 timer 的函數(shù)有兩個 dodeltimer,dodeltimer0。

2.6 Timer 是如何被真正執(zhí)行的?

timer 的真正執(zhí)行者是 GMP。GMP 會在每個調(diào)度周期內(nèi),通過 runtime.checkTimers 調(diào)用 timer.runtimer(). timer.runtimer 會檢查該 p 的 timer 堆上的所有 timer,判斷這些 timer 是否能被觸發(fā)。

如果該 timer 能夠被觸發(fā),會通過回調(diào)函數(shù) sendTime 給 Timer 的 channel C 發(fā)一個當前時間,告訴我們這個 timer 已經(jīng)被觸發(fā)了。

如果是 ticker 的話,被觸發(fā)后,會計算下一次要觸發(fā)的時間,重新將 timer 加入 timer 堆中。

3. Timer 使用中的坑

確實 timer 是我們開發(fā)中比較常用的工具,但是 timer 也是最容易導致內(nèi)存泄露,CPU 狂飆的殺手之一。

不過仔細分析可以發(fā)現(xiàn),其實能夠造成問題就兩個方面:

  • 錯誤創(chuàng)建很多的 timer,導致資源浪費
  • 由于 Stop 時不會主動關(guān)閉 C,導致程序阻塞

3.1 錯誤創(chuàng)建很多 timer,導致資源浪費

func main() {
    for {
        // xxx 一些操作
        timeout := time.After(30 * time.Second)
        select {
        case - someDone:
            // do something
        case -timeout:
            return
        }
    }
}

上面這段代碼是造成 timer 異常的最常見的寫法,也是我們最容易忽略的寫法。

造成問題的原因其實也很簡單,因為 timer.After 底層是調(diào)用的 timer.NewTimer,NewTimer 生成 timer 后,會將 timer 放入到全局的 timer 堆中。

for 會創(chuàng)建出來數(shù)以萬計的 timer 放入到 timer 堆中,導致機器內(nèi)存暴漲,同時不管 GMP 周期 checkTimers,還是插入新的 timer 都會瘋狂遍歷 timer 堆,導致 CPU 異常。

要注意的是,不只 time.After 會生成 timer, NewTimer,time.AfterFunc 同樣也會生成 timer 加入到 timer 中,也都要防止循環(huán)調(diào)用。

解決辦法: 使用 time.Reset 重置 timer,重復(fù)利用 timer。

我們已經(jīng)知道 time.Reset 會重新設(shè)置 timer 的觸發(fā)時間,然后將 timer 重新加入到 timer 堆中,等待被觸發(fā)調(diào)用。

func main() {
    timer := time.NewTimer(time.Second * 5)    
    for {
        t.Reset(time.Second * 5)

        select {
        case - someDone:
            // do something
        case -timer.C:
            return
        }
    }
}

3.2 程序阻塞,造成內(nèi)存或者 goroutine 泄露

func main() {
    timer1 := time.NewTimer(2 * time.Second)
    -timer1.C
    println("done")
}

上面的代碼可以看出來,只有等待 timer 超時 "done" 才會輸出,原理很簡單:程序阻塞在 -timer1.C 上,一直等待 timer 被觸發(fā)時,回調(diào)函數(shù) time.sendTime 才會發(fā)送一個當前時間到 timer1.C 上,程序才能繼續(xù)往下執(zhí)行。

不過使用 timer.Stop 的時候就要特別注意了,比如:

func main() {
    timer1 := time.NewTimer(2 * time.Second)
    go func() {
        timer1.Stop()
    }()
    -timer1.C

    println("done")
}

程序就會一直死鎖了,因為 timer1.Stop 并不會關(guān)閉 channel C,使程序一直阻塞在 timer1.C 上。

上面這個例子過于簡單了,試想下如果 - timer1.C 是阻塞在子協(xié)程中,timer 被的 Stop 方法被調(diào)用,那么子協(xié)程可能就會被永遠的阻塞在那里,造成 goroutine 泄露,內(nèi)存泄露。

Stop 的正確的使用方式:

func main() {
    timer1 := time.NewTimer(2 * time.Second)
    go func() {
        if !timer1.Stop() {
            -timer1.C
        }
    }()

    select {
    case -timer1.C:
        fmt.Println("expired")
    default:
    }
    println("done")
}

到此這篇關(guān)于Go timer如何調(diào)度 的文章就介紹到這了,更多相關(guān)Go timer 調(diào)度 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:
  • Golang 定時器(Timer 和 Ticker),這篇文章就夠了
  • go語言Timer計時器的用法示例詳解
  • go語言中使用timer的常用方式

標簽:嘉峪關(guān) 黑龍江 延邊 武漢 新余 宜賓 張掖 江西

巨人網(wǎng)絡(luò)通訊聲明:本文標題《Go timer如何調(diào)度》,本文關(guān)鍵詞  timer,如何,調(diào)度,timer,如何,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《Go timer如何調(diào)度》相關(guān)的同類信息!
  • 本頁收集關(guān)于Go timer如何調(diào)度的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    校园春色亚洲色图_亚洲视频分类_中文字幕精品一区二区精品_麻豆一区区三区四区产品精品蜜桃
    国产成人免费在线视频| 久久综合色一综合色88| 精品国产区一区| 亚洲欧美一区二区不卡| 激情图片小说一区| 欧美日韩dvd在线观看| 自拍偷拍亚洲激情| 国产精品综合网| 欧美一区二区啪啪| 亚洲国产中文字幕| 91丨porny丨在线| 国产日韩欧美高清| 狠狠色综合播放一区二区| 欧美日韩久久久久久| 最新热久久免费视频| 国产精品亚洲成人| 26uuu色噜噜精品一区| 日韩精品1区2区3区| 欧美影视一区在线| 亚洲蜜臀av乱码久久精品蜜桃| 国产99久久久久久免费看农村| 91精品国产综合久久精品app| 一区二区三区免费网站| 国产成人在线视频网址| 久久久三级国产网站| 国产精品综合一区二区三区| 精品国产乱码久久久久久牛牛| 日本不卡一二三| 91精品国产综合久久久久久久久久 | 国产精品乱人伦中文| 国产激情一区二区三区四区| 久久先锋影音av| 久久av中文字幕片| 欧美mv日韩mv国产网站| 蜜桃久久久久久| 欧美一级在线免费| 毛片一区二区三区| 日韩视频免费直播| 国产精品白丝jk黑袜喷水| 久久免费视频色| 成人黄色在线网站| 18成人在线观看| 欧美日韩国产一级| 麻豆91小视频| 国产欧美一区二区三区沐欲 | caoporn国产一区二区| 中文字幕在线观看一区二区| 91污片在线观看| 亚洲一区二区三区四区五区黄| 欧美日韩国产天堂| 久久精品噜噜噜成人88aⅴ| 欧美一区二区私人影院日本| 美国十次了思思久久精品导航| 精品久久久久一区| 成人动漫在线一区| 亚洲精品国产品国语在线app| 在线观看网站黄不卡| 午夜不卡av免费| 精品国产一区二区亚洲人成毛片| 国产在线精品免费av| 成人免费在线观看入口| 久久精品在线观看| 一本一本久久a久久精品综合麻豆| 亚洲主播在线播放| 精品国免费一区二区三区| 国产**成人网毛片九色 | 国产欧美日韩另类一区| 欧美最猛性xxxxx直播| 久久精品国产一区二区三| 中日韩av电影| 91麻豆精品国产自产在线观看一区| 久久99精品国产麻豆不卡| 国产精品青草综合久久久久99| 欧美日韩在线直播| 国产一区二区91| 亚洲成人免费看| 欧美国产激情一区二区三区蜜月| 欧美精品18+| 99精品欧美一区二区三区小说 | 亚洲精品视频免费观看| 精品欧美一区二区三区精品久久 | 日韩精品中午字幕| 欧美综合一区二区| 国产mv日韩mv欧美| 久色婷婷小香蕉久久| 亚洲一区二区三区四区五区中文| 亚洲午夜电影在线观看| 91视频91自| 久久99国产精品久久| 一级女性全黄久久生活片免费| 久久国产精品无码网站| 欧美在线不卡视频| 亚洲电影一级黄| 欧美日韩一区二区三区免费看| 一区二区三区四区在线播放| 91成人国产精品| 一二三四社区欧美黄| 日韩欧美一二三| 色综合久久久久| 国产精品毛片久久久久久| 精品久久久久久久久久久久包黑料| 日本二三区不卡| 成人网男人的天堂| 成人免费毛片高清视频| 国产精品99久久久久久似苏梦涵| 日本在线播放一区二区三区| 亚洲你懂的在线视频| 亚洲欧洲无码一区二区三区| 国产午夜精品一区二区三区视频| 欧美成人艳星乳罩| 久久亚洲综合色| 欧美精品一区二区在线观看| 欧美一区二区三区视频| 欧美一区二区三区免费视频 | 国产日韩成人精品| 国产丝袜美腿一区二区三区| 亚洲精品在线一区二区| 久久一日本道色综合| 久久久久一区二区三区四区| 久久久精品中文字幕麻豆发布| 久久影音资源网| 国产欧美日韩三区| 亚洲欧美一区二区久久| 一区二区三区中文字幕精品精品 | 欧美日本一区二区三区| 91精品国产高清一区二区三区| 91精品国产色综合久久不卡蜜臀| 欧美日韩国产高清一区| 欧美一区二区三区在线观看视频| 69久久夜色精品国产69蝌蚪网| 日韩精品一区二区在线观看| 久久这里只精品最新地址| 欧美激情一区在线观看| 亚洲美女屁股眼交| 日本91福利区| 国产不卡免费视频| 色综合天天天天做夜夜夜夜做| 在线免费观看一区| 精品国产乱码久久久久久久| 中文av字幕一区| 亚洲成av人在线观看| 国产一区二区在线看| eeuss鲁片一区二区三区| 欧美美女一区二区| 久久久久久黄色| 亚洲国产一区二区三区| 7777精品伊人久久久大香线蕉完整版 | 国产精品国产三级国产三级人妇| 国产欧美一区二区精品久导航| 欧美国产精品一区| 天天射综合影视| 国产成人精品网址| 日本成人在线一区| 亚洲成人三级小说| 韩国v欧美v日本v亚洲v| 99re这里只有精品首页| 91.com在线观看| 国产精品婷婷午夜在线观看| 石原莉奈在线亚洲三区| 国产成人精品免费视频网站| 欧美日韩一区二区电影| 久久精品男人天堂av| 丝袜美腿亚洲综合| 99久久久久免费精品国产| 欧美mv和日韩mv国产网站| 一区二区三区自拍| 国产99久久久国产精品| 在线综合+亚洲+欧美中文字幕| 国产精品情趣视频| 激情五月婷婷综合| 666欧美在线视频| 亚洲欧美激情视频在线观看一区二区三区| 日韩avvvv在线播放| 色狠狠色噜噜噜综合网| 久久精品一区二区三区不卡牛牛 | 国产九色sp调教91| 日韩视频国产视频| 亚洲丶国产丶欧美一区二区三区| 99在线精品免费| 久久蜜臀中文字幕| 久久国产精品免费| 欧美一激情一区二区三区| 一区二区高清视频在线观看| 丰满白嫩尤物一区二区| 亚洲精品一区二区三区福利| 三级一区在线视频先锋| 欧美性感一区二区三区| 亚洲精品国产一区二区三区四区在线| 国产成人高清视频| 久久久久久久久岛国免费| 经典三级视频一区| 337p粉嫩大胆色噜噜噜噜亚洲| 免费观看日韩电影| 欧美一级日韩免费不卡| 视频一区二区国产| 日韩三级视频中文字幕| 日韩二区三区在线观看| 制服丝袜一区二区三区| 午夜精品一区二区三区免费视频| 欧美亚洲自拍偷拍|