最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Golang實(shí)現(xiàn)帶優(yōu)先級(jí)的select

 更新時(shí)間:2023年04月02日 09:42:56   作者:jxwu  
這篇文章主要為大家詳細(xì)介紹了如何在Golang中實(shí)現(xiàn)帶優(yōu)先級(jí)的select,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Golang有一定的幫助,需要的可以參考一下

背景

在 Golang 里面,我們經(jīng)常使用 channel 進(jìn)行協(xié)程之間的通信。這里有一個(gè)經(jīng)典的場(chǎng)景,也就是生產(chǎn)者消費(fèi)者模式,生產(chǎn)者協(xié)程不斷地往 Channel 里面塞元素,而消費(fèi)者協(xié)程不斷地消費(fèi)這些元素。

寫(xiě)成代碼就是如下:

package main

import (
	"fmt"
	"sync"
	"time"
)

func main() {
	ch := make(chan int, 10)
	var wg sync.WaitGroup
	wg.Add(2)
	go producer(ch, &wg)
	go consumer(ch, &wg)
	wg.Wait()
}

// 生產(chǎn)者
func producer(ch chan int, wg *sync.WaitGroup) {
	defer wg.Done()
	i := 0
	for {
		select {
		case ch <- i:
		default:
			// 丟棄
			log.Println("discard")
		}
		i++
		time.Sleep(time.Second)
	}
}

// 消費(fèi)者
func consumer(ch chan int, wg *sync.WaitGroup) {
	defer wg.Done()
	consume := func(i int) {
		fmt.Println(i)
		time.Sleep(time.Millisecond * 700)
	}
	for {
		i := <-ch
		consume(i) // 消費(fèi)元素
	}
}

生產(chǎn)者不斷產(chǎn)生元素,消費(fèi)者消費(fèi)元素。生產(chǎn)者不會(huì)等待消費(fèi)者消費(fèi)完畢(不然可能影響其他任務(wù)),如果 channel 已經(jīng)滿了,也就是說(shuō)明消費(fèi)者消費(fèi)不過(guò)來(lái),生產(chǎn)者就會(huì)丟棄這個(gè)任務(wù)。

生產(chǎn)者平均一秒生成1個(gè),消費(fèi)者0.7秒消費(fèi)一個(gè)。正常情況下消費(fèi)者是消費(fèi)得過(guò)來(lái)的,然而很多時(shí)候消費(fèi)者協(xié)程還需要做一些定時(shí)任務(wù),比如一些定時(shí)清理工作。假如這個(gè)清理工作每2秒觸發(fā)一次,清理時(shí)間一般需要1.5秒,也就是如果每次都做每一秒有0.75秒會(huì)被清理工作占有了,但是它不是一定要非常及時(shí)的,可以等空閑時(shí)再進(jìn)行。 如下代碼:

// 消費(fèi)者
func consumer(ch chan int, wg *sync.WaitGroup) {
	defer wg.Done()
	t := time.NewTicker(time.Second * 2)
	consume := func(i int) {
		fmt.Println(i)
		time.Sleep(time.Millisecond * 700)
	}
	clear := func() {
		fmt.Println("clear")
		time.Sleep(time.Millisecond * 1500)
	}
	for {
		select {
		case i := <-ch:
			consume(i) // 消費(fèi)元素:
		case <-t.C:
			clear() // 清理
		}
	}
}

運(yùn)行程序到第15秒的時(shí)候,生產(chǎn)者發(fā)現(xiàn) channel滿了,于是開(kāi)始丟包:

0
1
clear
2
3
4
5
6
clear
7
clear
8
clear
9
clear
clear
10
clear
11
12
13
14
clear
15
clear
clear
discard
16
clear
discard
discard

解決方案

既然清理任務(wù)的優(yōu)先級(jí)并不高,那么它就不應(yīng)該阻塞消費(fèi)元素流程,而是應(yīng)該在空閑時(shí)才去執(zhí)行。由于 Golang 里面,如果 select 兩個(gè) case 都同時(shí)滿足,會(huì)隨機(jī)選一個(gè)執(zhí)行,因此第一想到的可能會(huì)使用如下代碼實(shí)現(xiàn)優(yōu)先級(jí)case:

// 消費(fèi)者
func consumer(ch chan int, wg *sync.WaitGroup) {
	defer wg.Done()
	t := time.NewTicker(time.Second * 2)
	consume := func(i int) {
		fmt.Println(i)
		time.Sleep(time.Millisecond * 700)
	}
	clear := func() {
		fmt.Println("clear")
		time.Sleep(time.Millisecond * 1500)
	}
	for {
		select {
		case i := <-ch:
			consume(i) // 消費(fèi)元素
			continue   // 可能還有元素,不走清理邏輯
		default:
		}

		// 沒(méi)有元素才走清理邏輯
		select {
		case <-t.C:
			clear() // 清理
		default:
		}
	}
}

如果運(yùn)行這個(gè)程序,可以發(fā)現(xiàn)它能夠滿足優(yōu)先級(jí)的需求,先消費(fèi)元素,空閑時(shí)再執(zhí)行清理任務(wù)。

然而,在沒(méi)有元素可以消費(fèi),也沒(méi)有清理任務(wù)可以執(zhí)行的時(shí)候,這里的for將會(huì)不斷地循環(huán),浪費(fèi)CPU資源。

其實(shí),可以使用下面的方法實(shí)現(xiàn)優(yōu)先級(jí)case,它能夠在沒(méi)有元素就緒的時(shí)候阻塞在 select,而不是不斷循環(huán):

// 消費(fèi)者
func consumer(ch chan int, wg *sync.WaitGroup) {
	defer wg.Done()
	t := time.NewTicker(time.Second * 2)
	consume := func(i int) {
		fmt.Println(i)
		time.Sleep(time.Millisecond * 700)
	}
	clear := func() {
		fmt.Println("clear")
		time.Sleep(time.Millisecond * 1500)
	}
	for {
		select {
		case i := <-ch:
			consume(i) // 消費(fèi)元素
		case <-t.C:
		priority:
			for { // 清理前先把元素消費(fèi)完
				select {
				case i := <-ch:
					consume(i) // 消費(fèi)元素
				default:
					break priority // 注:這里會(huì)跳過(guò)這個(gè)循環(huán),而不是再次執(zhí)行
				}
			}
			clear() // 清理
		}
	}
}

這里的關(guān)鍵是在觸發(fā)清理case的時(shí)候,先去把channel里面的元素消費(fèi)完,再進(jìn)行清理,從而保證能夠留下足夠的channel緩沖區(qū)給生產(chǎn)者放置生產(chǎn)的元素。

一個(gè)封裝

上面那段優(yōu)先級(jí)case代碼其實(shí)挺常用的,但是幾乎都是模板代碼,特別是需要在兩個(gè)地方寫(xiě)consume(i),因此我們可以封裝一下這段代碼,方便使用,減少出錯(cuò):

// 優(yōu)先級(jí)select ch1 的任務(wù)先執(zhí)行完畢后才會(huì)執(zhí)行 ch2 里面的任務(wù)
func PrioritySelect[T1, T2 any](ch1 <-chan T1, f1 func(T1), ch2 <-chan T2, f2 func(T2)) {
	for {
		select {
		case a := <-ch1:
			f1(a)
		case b := <-ch2:
		priority:
			for {
				select {
				case a := <-ch1:
					f1(a)
				default:
					break priority
				}
			}
			f2(b)
		}
	}
}

這樣,我們的消費(fèi)者代碼就可以簡(jiǎn)化為:

// 消費(fèi)者
func consumer(ch chan int, wg *sync.WaitGroup) {
	defer wg.Done()
	t := time.NewTicker(time.Second * 2)
	consume := func(i int) {
		fmt.Println(i)
		time.Sleep(time.Millisecond * 700)
	}
	clear := func(time.Time) {
		fmt.Println("clear")
		time.Sleep(time.Millisecond * 1500)
	}
	PrioritySelect(ch, consume, t.C, clear)
}

到此這篇關(guān)于Golang實(shí)現(xiàn)帶優(yōu)先級(jí)的select的文章就介紹到這了,更多相關(guān)Golang帶優(yōu)先級(jí)select內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • GO語(yǔ)言利用K近鄰算法實(shí)現(xiàn)小說(shuō)鑒黃

    GO語(yǔ)言利用K近鄰算法實(shí)現(xiàn)小說(shuō)鑒黃

    本文給大家分享的是一段GO語(yǔ)言利用K近鄰算法實(shí)現(xiàn)小說(shuō)鑒黃的方法,本方法的鑒別的關(guān)鍵是關(guān)鍵是向量點(diǎn)的選擇和閾值的判定,推薦給大家,有需要的小伙伴可以參考下。
    2015-03-03
  • Go map排序的實(shí)現(xiàn)示例

    Go map排序的實(shí)現(xiàn)示例

    map默認(rèn)是無(wú)序的,不管是按照key還是按照value默認(rèn)都不排序,本文主要介紹了Go map排序的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-12-12
  • 詳解go中panic源碼解讀

    詳解go中panic源碼解讀

    這篇文章主要介紹了go中panic源碼解讀,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • Go語(yǔ)言Handler詳細(xì)說(shuō)明

    Go語(yǔ)言Handler詳細(xì)說(shuō)明

    這篇文章主要介紹了Go語(yǔ)言Handler詳細(xì)說(shuō)明,Handler用于處理請(qǐng)求并給予響應(yīng)。更嚴(yán)格地說(shuō),用來(lái)讀取請(qǐng)求體、并將請(qǐng)求對(duì)應(yīng)的響應(yīng)字段(respones header)寫(xiě)入ResponseWriter中,需要的朋友可以參考下
    2022-04-04
  • 一文詳解Golang的模塊版本管理與語(yǔ)義版本控制

    一文詳解Golang的模塊版本管理與語(yǔ)義版本控制

    在Golang中,模塊(module)是Go 1.11版本引入的依賴管理系統(tǒng),幫助開(kāi)發(fā)者管理項(xiàng)目的依賴,在Go模塊推出之前,開(kāi)發(fā)者通常使用GOPATH和vendor目錄來(lái)管理項(xiàng)目的依賴,本文將給大家詳細(xì)介紹Golang的模塊版本管理與語(yǔ)義版本控制,需要的朋友可以參考下
    2023-12-12
  • golang操作rocketmq的示例代碼

    golang操作rocketmq的示例代碼

    這篇文章主要介紹了golang操作rocketmq的示例代碼,代碼簡(jiǎn)單易懂,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-04-04
  • Go語(yǔ)言編程入門(mén)超級(jí)指南

    Go語(yǔ)言編程入門(mén)超級(jí)指南

    這篇文章主要介紹了Go語(yǔ)言編程的入門(mén)指南,包括對(duì)Go的變量及函數(shù)的基本介紹,需要的朋友可以參考下
    2016-01-01
  • 詳解Go 并發(fā)

    詳解Go 并發(fā)

    這篇文章主要介紹了Go 并發(fā)的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)go語(yǔ)言,感興趣的朋友可以了解下
    2020-09-09
  • 利用Golang實(shí)現(xiàn)對(duì)配置文件加密

    利用Golang實(shí)現(xiàn)對(duì)配置文件加密

    在實(shí)際的應(yīng)用中,配置文件通常包含了一些敏感的信息,如數(shù)據(jù)庫(kù)密碼、API密鑰等,為了保護(hù)這些敏感信息不被惡意獲取,我們可以對(duì)配置文件進(jìn)行加密,本文將介紹如何使用Go語(yǔ)言實(shí)現(xiàn)對(duì)配置文件的加密,需要的朋友可以參考下
    2023-10-10
  • 詳解Golang如何實(shí)現(xiàn)一個(gè)環(huán)形緩沖器

    詳解Golang如何實(shí)現(xiàn)一個(gè)環(huán)形緩沖器

    環(huán)形緩沖器(ringr?buffer)是一種用于表示一個(gè)固定尺寸、頭尾相連的緩沖區(qū)的數(shù)據(jù)結(jié)構(gòu),適合緩存數(shù)據(jù)流。本文將利用Golang實(shí)現(xiàn)一個(gè)環(huán)形緩沖器,需要的可以參考一下
    2022-09-09

最新評(píng)論

壶关县| 美姑县| 安西县| 武陟县| 察隅县| 息烽县| 古交市| 岳阳市| 贵定县| 三亚市| 堆龙德庆县| 安泽县| 民和| 乌审旗| 习水县| 莱西市| 沙坪坝区| 达孜县| 岑溪市| 雅安市| 杭州市| 武山县| 遵义县| 木兰县| 文水县| 青龙| 紫云| 泾阳县| 隆昌县| 砀山县| 邹平县| 垣曲县| 长葛市| 织金县| 贵南县| 南部县| 甘肃省| 会东县| 商南县| 房山区| 南京市|