溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Go語(yǔ)言協(xié)程處理數(shù)據(jù)問(wèn)題怎么解決

發(fā)布時(shí)間:2023-02-25 10:54:17 來(lái)源:億速云 閱讀:133 作者:iii 欄目:開(kāi)發(fā)技術(shù)

本篇內(nèi)容主要講解“Go語(yǔ)言協(xié)程處理數(shù)據(jù)問(wèn)題怎么解決”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“Go語(yǔ)言協(xié)程處理數(shù)據(jù)問(wèn)題怎么解決”吧!

一、Goroutine

當(dāng)然第一個(gè)想到可能是采用協(xié)程處理循環(huán)里面要查詢的數(shù)據(jù)

type Card struct {
	Name    string  `json:"name"`
	Balance float64 `json:"balance"`
}
func main() {
	// 獲取卡列表數(shù)據(jù)
	list := getList()
	var data = make([]Card, 0, len(list))
	for _, val := range list {
		go func(card Card) {
			// 查詢業(yè)務(wù),將值加入該記錄中
			var balance = getBalance()
			data = append(data, Card{
				Name:    card.Name,
				Balance: balance,
			})
		}(val)
	}
	log.Printf("數(shù)據(jù):%+v", data)
}
// 獲取數(shù)據(jù)列表
func getList() []Card {
	var list = make([]Card, 0)
	for i := 0; i < 10000; i++ {
		list = append(list, Card{
			Name: "卡-" + strconv.Itoa(i+1),
		})
	}
	return list
}
// 獲取余額
func getBalance() float64 {
	time.Sleep(time.Millisecond * 100)
	return float64(rand.Int63n(1000))
}

運(yùn)行上述代碼,結(jié)果: "數(shù)據(jù):[]",這是為什么呢?主要是協(xié)程處理業(yè)務(wù)需要時(shí)間,循環(huán)提前結(jié)束,所以才會(huì)出現(xiàn)這樣的結(jié)果,該怎么讓所有結(jié)果都處理結(jié)束才輸出結(jié)果呢?

二、sync.WaitGroup

此方法就是等待組進(jìn)行多個(gè)任務(wù)的同步,等待組可以保證在并發(fā)環(huán)境中完成指定數(shù)量的任務(wù)

func main() {
	list := getList() // 獲取卡列表數(shù)據(jù)
	var data = make([]Card, 0, len(list))
	var wg sync.WaitGroup // 聲明一個(gè)等待組
	for _, val := range list {
		wg.Add(1) // 每一個(gè)任務(wù)開(kāi)始時(shí),將等待組增加1
		go func(card Card) {
			defer wg.Done() // 使用defer, 表示函數(shù)完成時(shí)將等待組值減1
			// 查詢業(yè)務(wù),休眠100微妙,將值加入該記錄中
			var balance = getBalance()
			data = append(data, Card{
				Name:    card.Name,
				Balance: balance,
			})
		}(val)
	}
	wg.Wait() // 等待所有任務(wù)完成
	log.Printf("數(shù)據(jù):%+v", data)
}

運(yùn)行結(jié)果會(huì)輸出所有數(shù)據(jù),但細(xì)心的我們會(huì)發(fā)現(xiàn),這個(gè)時(shí)候數(shù)據(jù)的順序是亂的,這個(gè)也符合業(yè)務(wù)需求,該怎么進(jìn)一步改良呢?

三、數(shù)據(jù)排序

上面講到協(xié)程處理之后的額數(shù)據(jù)是無(wú)序的,這里我們知道數(shù)據(jù)跳數(shù),直接初始化一個(gè)len和cap等于len(list)的空間,將之前append到data的數(shù)據(jù)改成通過(guò)下標(biāo)復(fù)制,這樣輸出的數(shù)據(jù)就是list的數(shù)據(jù)順序。

func main() {
	list := getList() // 獲取卡列表數(shù)據(jù)
	var data = make([]Card, len(list), len(list))
	var wg sync.WaitGroup // 聲明一個(gè)等待組
	for k, val := range list {
		wg.Add(1) // 每一個(gè)任務(wù)開(kāi)始時(shí),將等待組增加1
		go func(k int, card Card) {
			defer wg.Done() // 使用defer, 表示函數(shù)完成時(shí)將等待組值減1
			// 查詢業(yè)務(wù),休眠100微妙,將值加入該記錄中
			var balance = getBalance()
			data[k] = Card{
				Name:    card.Name,
				Balance: balance,
			}
		}(k, val)
	}
	wg.Wait() // 等待所有任務(wù)完成
	log.Printf("數(shù)據(jù):%+v", data)
}

運(yùn)行上述代碼,雖然可以獲取到想要的數(shù)據(jù)排序,但下次下載數(shù)據(jù)較多,開(kāi)的協(xié)程過(guò)多,勢(shì)必導(dǎo)致資源開(kāi)銷過(guò)大,帶來(lái)一系列問(wèn)題,那怎么優(yōu)化限制協(xié)程個(gè)數(shù)呢?

四、限制協(xié)程數(shù)

大家都知道協(xié)程過(guò)多,自然消耗過(guò)多資源,可能導(dǎo)致其他問(wèn)題;這里我們借助chan限制協(xié)程個(gè)數(shù)

// 限制100個(gè)協(xié)程
type pool struct {
	queue chan int
	wg    *sync.WaitGroup
}
func main() {
	list := getList() // 獲取卡列表數(shù)據(jù)
	var data = make([]Card, len(list), len(list))
	var gl = &pool{queue: make(chan int, 500), wg: &sync.WaitGroup{}} // 顯示協(xié)程數(shù)最大500個(gè)
	for k, val := range list {
		gl.queue <- 1 // 每一個(gè)任務(wù)開(kāi)始時(shí), chan輸入1個(gè)
		gl.wg.Add(1)  // 每一個(gè)任務(wù)開(kāi)始時(shí),將等待組增加1
		go func(k int, card Card) {
			defer func() {
				<-gl.queue   // 完成時(shí)chan取出1個(gè)
				gl.wg.Done() // 完成時(shí)將等待組值減1
			}()
			// 查詢業(yè)務(wù),休眠100微妙,將值加入該記錄中
			var balance = getBalance()
			data[k] = Card{
				Name:    card.Name,
				Balance: balance,
			}
		}(k, val)
	}
	gl.wg.Wait() // 等待所有任務(wù)完成
	log.Printf("數(shù)據(jù):%+v", data)
}

通過(guò)使用chan,可以自己定義可協(xié)程最大數(shù);現(xiàn)在看起來(lái)沒(méi)有什么問(wèn)題,但如果協(xié)程獲取數(shù)據(jù)panic,會(huì)導(dǎo)致整個(gè)程序崩潰。

五、協(xié)程Panic處理

針對(duì)協(xié)程的panic(),我們需要接收,使用recover處理

func main() {
	list := getList() // 獲取卡列表數(shù)據(jù)
	var data = make([]Card, len(list), len(list))
	var gl = &pool{queue: make(chan int, 500), wg: &sync.WaitGroup{}} // 顯示協(xié)程數(shù)最大500個(gè)
	for k, val := range list {
		gl.queue <- 1 // 每一個(gè)任務(wù)開(kāi)始時(shí), chan輸入1個(gè)
		gl.wg.Add(1)  // 每一個(gè)任務(wù)開(kāi)始時(shí),將等待組增加1
		go func(k int, card Card) {
			// 解決協(xié)程panic,不至于程序崩潰
			defer func() {
				recover()
			}()
			defer func() {
				<-gl.queue   // 完成時(shí)chan取出1個(gè)
				gl.wg.Done() // 完成時(shí)將等待組值減1
			}()
			// 查詢業(yè)務(wù),休眠100微妙,將值加入該記錄中
			var balance = getBalance()
			data[k] = Card{
				Name:    card.Name,
				Balance: balance,
			}
		}(k, val)
	}
	gl.wg.Wait() // 等待所有任務(wù)完成
	log.Printf("數(shù)據(jù):%+v", data)
}
// 獲取余額
func getBalance() float64 {
	panic("獲取余額panic")
	time.Sleep(time.Millisecond * 100)
	return float64(rand.Int63n(1000))
}

在協(xié)程中使用defer recover();這樣協(xié)程拋出來(lái)的panic被接受,不會(huì)導(dǎo)致程序奔潰。

到此,相信大家對(duì)“Go語(yǔ)言協(xié)程處理數(shù)據(jù)問(wèn)題怎么解決”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI