怎么理解并掌握的Go高級(jí)并發(fā)模式計(jì)時(shí)器

這篇文章主要講解了“怎么理解并掌握的Go高級(jí)并發(fā)模式計(jì)時(shí)器”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“怎么理解并掌握的Go高級(jí)并發(fā)模式計(jì)時(shí)器”吧!

創(chuàng)新互聯(lián)是一家網(wǎng)站設(shè)計(jì)公司,集創(chuàng)意、互聯(lián)網(wǎng)應(yīng)用、軟件技術(shù)為一體的創(chuàng)意網(wǎng)站建設(shè)服務(wù)商,主營產(chǎn)品:成都響應(yīng)式網(wǎng)站建設(shè)、成都品牌網(wǎng)站建設(shè)、成都全網(wǎng)營銷推廣。我們專注企業(yè)品牌在網(wǎng)站中的整體樹立,網(wǎng)絡(luò)互動(dòng)的體驗(yàn),以及在手機(jī)等移動(dòng)端的優(yōu)質(zhì)呈現(xiàn)。成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè)、移動(dòng)互聯(lián)產(chǎn)品、網(wǎng)絡(luò)運(yùn)營、VI設(shè)計(jì)、云產(chǎn)品.運(yùn)維為核心業(yè)務(wù)。為用戶提供一站式解決方案,我們深知市場(chǎng)的競(jìng)爭(zhēng)激烈,認(rèn)真對(duì)待每位客戶,為客戶提供賞析悅目的作品,網(wǎng)站的價(jià)值服務(wù)。

前言

如果你認(rèn)為結(jié)合 Goroutines 去處理時(shí)間和計(jì)數(shù)器很簡單的話,那你就錯(cuò)了,這里有提到的一些與 time.Timer 相關(guān)的問題或 bug:

  • time: Timer.Reset is not possible to use correctly #14038[1]

  • time: Timer.C can still trigger even after Timer.Reset is called  #11513[2]

  • time: document proper usage of Timer.Stop #14383[3]

看完上面的鏈接內(nèi)容后,如果你依然認(rèn)為很簡單,那來看看下面的代碼,如下代碼會(huì)產(chǎn)生死鎖和競(jìng)爭(zhēng)條件

tm := time.NewTimer(1)tm.Reset(100 * time.Millisecond)<-tm.Cif !tm.Stop() {<-tm.C}

死鎖代碼片段

func toChanTimed(t *time.Timer, ch chan int) {t.Reset(1 * time.Second)defer func() {if !t.Stop() {<-t.C}}()select {case ch <- 42:case <-t.C:}}

可能代碼比較難懂,下面對(duì)相關(guān)方法進(jìn)行闡述。

time.Ticker

type Ticker struct {C <-chan Time // The channel on which the ticks are delivered.}

Ticker 簡單易用,但也有一些小問題

  • 如果 C 中已存在一條消息,則發(fā)送消息時(shí)將刪除所有未讀值。

  • 必須有停止操作:否則 GC 無法回收它

  • 設(shè)置 C 無用:消息仍將在原始的 channel 上發(fā)送。

time.Tick

time.Tick 是對(duì) time.NewTicker 的封裝。最好不要使用該方法,除非你準(zhǔn)備將 chan  作為返回結(jié)果并在程序的整個(gè)生命周期中繼續(xù)使用它。正如官方描述:

垃圾收集器無法恢復(fù)底層的 Ticker,出現(xiàn) " 泄漏 ". 請(qǐng)謹(jǐn)慎使用,如有疑問請(qǐng)改用 Ticker。

time.After

這與 Tick 的概念基本相同,它是對(duì) Timer 進(jìn)行封裝。一旦計(jì)時(shí)器被觸發(fā),它將被回收。請(qǐng)注意,計(jì)時(shí)器使用了緩存容量是 1  的通道,即使沒有接收者,它仍可以進(jìn)行計(jì)數(shù)。如上所述,如果您關(guān)心性能且希望能夠取消計(jì)時(shí),那么你不應(yīng)該使用 After。

time.Timer ( 也稱為 time.WhatTheFork?!)

對(duì)于 Go 來說這是一個(gè)比較奇怪的 API :NewTicker(Duration) 返回了一個(gè) *Timer 類型,該類型僅暴露一個(gè)定義為 chan  類型的變量 C ,這點(diǎn)非常奇怪。

通常在 Go 語言中允許導(dǎo)出的字段意味著用戶可以獲取或設(shè)置該字段,而此處設(shè)置變量 C 并沒有實(shí)際意義。相反:設(shè)置 C 并重置 Timer 并不會(huì)影響之前在  C 通道的消息傳遞。更糟糕的是:AfterFunc 返回的 Timer 根本不會(huì)使用到 C。

這樣看來,Timer 很奇怪,以下是 API 的概述:

type Timer struct {C <-chan Time}func AfterFunc(d Duration, f func()) *Timerfunc NewTimer(d Duration) *Timerfunc (*Timer) Stop(bool)func (*Timer) Reset(d Duration) bool

四個(gè)非常簡單的函數(shù),其中兩個(gè)是構(gòu)造函數(shù),有可能出錯(cuò)嗎?

time.AfterFunc

官方文檔:AfterFunc 持續(xù)時(shí)間超時(shí)后通過開 Goroutine 去調(diào)用 f 函數(shù),返回一個(gè) Timer 類型,以便通過 Stop  方法取消調(diào)用。

這么描述雖然沒有問題,但需要注意:當(dāng)調(diào)用 Stop 方法時(shí),如果返回 false  ,則表示該函數(shù)已經(jīng)執(zhí)行且停止失敗。但并不意味著函數(shù)已經(jīng)返回,你需要添加一些處理邏輯:

done := make(chan struct{})f := func() {doStuff()close(done)}t := time.AfterFunc(1*time.Second, f)if !t.Stop() {<-done}

這個(gè)在 Stop 文檔中有相關(guān)說明。

除此之外,返回的計(jì)時(shí)器不會(huì)被觸發(fā),只能用于調(diào)用 Stop 方法。

t := time.AfterFunc(1*time.Second, func() {fmt.Println("Time has passed!")})// This will deadlock.<-t.C

此外,寫這篇文章的時(shí)候,重置計(jì)時(shí)器會(huì)在傳入重置函數(shù)的時(shí)間段過去后再次調(diào)用 f,但這種特性目前暫沒有文檔規(guī)范,未來可能會(huì)被改變。

time.NewTimer

官方文檔 : NewTimer 實(shí)例化 Timer 結(jié)構(gòu)體,在持續(xù)時(shí)間 d 之后發(fā)送當(dāng)前時(shí)間至通道內(nèi) .

這意味著沒有聲明它就無法構(gòu)建有效的 Timer  類型結(jié)構(gòu)體。如果你需要構(gòu)建一個(gè)以便后續(xù)重復(fù)使用,可以用該方法進(jìn)行實(shí)例化,或者使用如下代碼實(shí)現(xiàn)自主創(chuàng)建和停止計(jì)數(shù)器

t := time.NewTimer(0)if !t.Stop() {<-t.C}

你必須從 channel 中讀取數(shù)據(jù)。假如在 New 和 Stop 調(diào)用期間觸發(fā)了定時(shí)器,且 channel 存在未消費(fèi)的數(shù)據(jù), 則 C  會(huì)存在一個(gè)值。將導(dǎo)致后續(xù)讀取均是錯(cuò)誤的。

(*time.Timer).Stop

Stop 方法會(huì)阻止計(jì)時(shí)器觸發(fā)。如果調(diào)用停止計(jì)時(shí)器的方法,則返回 true,如果計(jì)時(shí)器已超時(shí)或者已停止,則返回 false。

以上句子中的“或”非常重要。文檔中所以關(guān)于 Stop 的示例都顯示了以下代碼片段:

if !t.Stop() {<-t.C}

關(guān)鍵點(diǎn)在于 "or" 它意味著有效 0 次或 1 次。對(duì)已消費(fèi)完通道數(shù)據(jù)和在此期間未調(diào)用 Reset  進(jìn)行過多次執(zhí)行的情況,均是無效的。綜上所述,當(dāng)且僅當(dāng)沒有執(zhí)行對(duì)通道數(shù)據(jù)的消費(fèi),Stop+drain 才是安全的。

在文檔中體現(xiàn)如下:

例如:假設(shè)程序尚未從 t.C 接收數(shù)據(jù):

此外,上面的模式不是線程安全的,因?yàn)楫?dāng)消費(fèi)完通道數(shù)據(jù)時(shí),Stop 返回的值可能已經(jīng)過時(shí)了,兩個(gè) Goroutine 嘗試消費(fèi)通道 C  數(shù)據(jù)也會(huì)導(dǎo)致死鎖。

(*time.Timer).Reset

這個(gè)方法更有意思,文檔很長,你可以在這里[4] 進(jìn)行查看

文檔中一個(gè)有趣的摘錄:

請(qǐng)注意,因?yàn)樵谇蹇?channel 和計(jì)數(shù)器到期之間存在競(jìng)爭(zhēng)條件,我們無法正確使用 Reset 返回值。Reset 方法必須作用于已停止或已過期的  channel 上。

文檔所提供 Reset 正確使用方法如下:

if !t.Stop() {<-t.C}t.Reset(d)

不能與來自通道的其他接收者同時(shí)使用 Stop 和 Reset 方法, 為了使 C 上傳遞的消息有效,C 應(yīng)該在每次 重置 之前被消費(fèi)完。

重置計(jì)時(shí)器而不清空它將使運(yùn)行過程時(shí)丟棄該值,因?yàn)?C 緩存為 1,運(yùn)行時(shí)對(duì)其他執(zhí)行是有損發(fā)送[5]。

time.Timer: 把這些方法放在一起

  • Stop 僅作用在 New 和 Reset 方法之后才安全

  • Reset 僅在 Stop 方法后有效。

  • 只有在每次運(yùn)行 Stop 后,channel 消費(fèi)完時(shí),所接收的值才是有效的。

  • 只有 channel 未被消費(fèi)時(shí),才允許清空 channel。

timer.png

如下是一個(gè)正確復(fù)用計(jì)時(shí)器的例子,它解決了文章開頭提到的一些問題:

func toChanTimed(t *time.Timer, ch chan int) {t.Reset(1 * time.Second)// No defer, as we don't know which// case will be selectedselect {case ch <- 42:case <-t.C:// C is drained, early returnreturn}// We still need to check the return value// of Stop, because t could have fired// between the send on ch and this line.if !t.Stop() {<-t.C}}

上述代碼可以確保 toChanTimed 返回后可以重新使用計(jì)時(shí)器

感謝各位的閱讀,以上就是“怎么理解并掌握的Go高級(jí)并發(fā)模式計(jì)時(shí)器”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)怎么理解并掌握的Go高級(jí)并發(fā)模式計(jì)時(shí)器這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

本文名稱:怎么理解并掌握的Go高級(jí)并發(fā)模式計(jì)時(shí)器
網(wǎng)頁地址:http://bm7419.com/article44/jdedhe.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站、響應(yīng)式網(wǎng)站、移動(dòng)網(wǎng)站建設(shè)、網(wǎng)站收錄、靜態(tài)網(wǎng)站、品牌網(wǎng)站設(shè)計(jì)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

小程序開發(fā)

網(wǎng)站設(shè)計(jì)公司知識(shí)