欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

go語(yǔ)言解決并發(fā)問(wèn)題小結(jié)

 更新時(shí)間:2024年05月20日 10:17:41   作者:snail_lie  
并發(fā)是GO最基本的功能了,但是在傳統(tǒng)的PHP中是比較困難的,如果不借助其它一些擴(kuò)展的話,是做不到并發(fā)的,這篇文章主要介紹了go語(yǔ)言如何解決并發(fā)問(wèn)題,需要的朋友可以參考下

一、選擇GO的原因

作為一個(gè)后端開(kāi)發(fā),日常工作中接觸最多的兩門語(yǔ)言就是PHP和GO了。無(wú)可否認(rèn),PHP確實(shí)是最好的語(yǔ)言(手動(dòng)狗頭哈哈),寫起來(lái)真的很舒爽,沒(méi)有任何心智負(fù)擔(dān),字符串和整型壓根就不用區(qū)分,開(kāi)發(fā)速度真的是比GO快很多。現(xiàn)在工作中也還是有一些老項(xiàng)目在使用PHP,但21年之后的新項(xiàng)目基本上就都是用GO了。那為什么PHP那么香,還要轉(zhuǎn)戰(zhàn)使用GO呢,下面就給大家講解一下我們新項(xiàng)目從PHP轉(zhuǎn)GO的原因,有幾個(gè)比較重要的點(diǎn):

1、PHP不能滿足我們的高并發(fā)業(yè)務(wù),這是最主要的原因了,(PS:我這里所說(shuō)的PHP是指官方的php-fpm模式下的開(kāi)發(fā),是一個(gè)請(qǐng)求一個(gè)進(jìn)程的那種模式,而不是類似于swoole常駐進(jìn)程的那種。那么為什么不去使用swoole呢,當(dāng)然也是有的,但swoole畢竟太小眾了,且之前有很多bug,使用起來(lái)心智負(fù)擔(dān)太高了),而我們部門所負(fù)責(zé)的是直播業(yè)務(wù),每天都和高并發(fā)打交道啊,所以只能將目光轉(zhuǎn)向了并發(fā)小王子GO的懷抱。

2、GO語(yǔ)言當(dāng)時(shí)在市面上很火,像騰訊、百度、滴滴、好未來(lái)這些大廠都在陸陸續(xù)續(xù)地從PHP轉(zhuǎn)向GO,這也是一個(gè)訊號(hào)吧,跟著大佬們走總不會(huì)錯(cuò)。

3、GO語(yǔ)言的簡(jiǎn)單簡(jiǎn)潔,相比較于JAVA,上手是很快的(但真正學(xué)好還是沒(méi)那么容易的),我當(dāng)時(shí)就學(xué)了兩個(gè)禮拜左右語(yǔ)法就跟著一起寫項(xiàng)目了。

二、GO解決的并發(fā)問(wèn)題

說(shuō)到并發(fā),是GO最基本的功能了,但是在傳統(tǒng)的PHP中是比較困難的,如果不借助其它一些擴(kuò)展的話,是做不到并發(fā)的。舉個(gè)場(chǎng)景:每個(gè)用戶進(jìn)入直播間,都要獲取很多信息,有版本服務(wù)信息、直播基礎(chǔ)信息、用戶信息、直播關(guān)聯(lián)權(quán)益信息、直播間信息統(tǒng)計(jì)等等。如果是PHP的寫法,就得按照下面串行的流程去做,這個(gè)接口耗時(shí)就是所有操作的時(shí)間之和,嚴(yán)重影響用戶體驗(yàn)啊。

但如果換成GO去做這件事,那就非常清爽了,這個(gè)用戶請(qǐng)求耗時(shí)就只需要時(shí)間最長(zhǎng)的那個(gè)操作耗時(shí),如下圖:

那么我們?nèi)绾斡萌?shí)現(xiàn)這個(gè)并發(fā)邏輯呢?

方法1:使用sync.WaitGroup

//請(qǐng)求入口
func main() {
	var (
		VersionDetail, LiveDetail, UserDetail, EquityDetail, StatisticsDetail int
	)
	ctx := context.Background()
	GoNoErr(ctx, func() {
		VersionDetail = 1 //版本服務(wù)信息
		time.Sleep(1 * time.Second)
		fmt.Println("執(zhí)行第一個(gè)任務(wù)")
	}, func() {
		LiveDetail = 2 //直播基礎(chǔ)信息
		time.Sleep(2 * time.Second)
		fmt.Println("執(zhí)行第二個(gè)任務(wù)")
	}, func() {
		UserDetail = 3 //用戶信息
		time.Sleep(3 * time.Second)
		fmt.Println("執(zhí)行第三個(gè)任務(wù)")
	}, func() {
		EquityDetail = 4 //直播關(guān)聯(lián)權(quán)益信息
		time.Sleep(4 * time.Second)
		fmt.Println("執(zhí)行第四個(gè)任務(wù)")
	}, func() {
		StatisticsDetail = 5 //直播間信息統(tǒng)計(jì)
		time.Sleep(5 * time.Second)
		fmt.Println("執(zhí)行第五個(gè)任務(wù)")
	})
	fmt.Println(VersionDetail, LiveDetail, UserDetail, EquityDetail, StatisticsDetail)
}
//并發(fā)方法
func GoNoErr(ctx context.Context, functions ...func()) {
	var wg sync.WaitGroup
	for _, f := range functions {
		wg.Add(1)
		// 每個(gè)函數(shù)啟動(dòng)一個(gè)協(xié)程
		go func(function func()) {
			function()
			wg.Done()
		}(f)
	}
	// 等待執(zhí)行完
	wg.Wait()
}

方法2:使用ErrGroup庫(kù)

//請(qǐng)求入口
func main() {
	var (
		VersionDetail, LiveDetail, UserDetail, EquityDetail, StatisticsDetail int
		err                                                                   error
	)
	ctx := context.Background()
	err = GoErr(ctx, func() error {
		VersionDetail = 1 //版本服務(wù)信息
		time.Sleep(1 * time.Second)
		fmt.Println("執(zhí)行第一個(gè)任務(wù)")
		return nil //返回實(shí)際執(zhí)行的錯(cuò)誤
	}, func() error {
		LiveDetail = 2 //直播基礎(chǔ)信息
		time.Sleep(2 * time.Second)
		fmt.Println("執(zhí)行第二個(gè)任務(wù)")
		return nil //返回實(shí)際執(zhí)行的錯(cuò)誤
	}, func() error {
		UserDetail = 3 //用戶信息
		time.Sleep(3 * time.Second)
		fmt.Println("執(zhí)行第三個(gè)任務(wù)")
		return nil //返回實(shí)際執(zhí)行的錯(cuò)誤
	}, func() error {
		EquityDetail = 4 //直播關(guān)聯(lián)權(quán)益信息
		time.Sleep(4 * time.Second)
		fmt.Println("執(zhí)行第四個(gè)任務(wù)")
		return nil //返回實(shí)際執(zhí)行的錯(cuò)誤
	}, func() error {
		StatisticsDetail = 5 //直播間信息統(tǒng)計(jì)
		time.Sleep(5 * time.Second)
		fmt.Println("執(zhí)行第五個(gè)任務(wù)")
		return nil //返回實(shí)際執(zhí)行的錯(cuò)誤
	})
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(VersionDetail, LiveDetail, UserDetail, EquityDetail, StatisticsDetail)
}
func GoErr(ctx context.Context, functions ...func() error) error {
	var eg errgroup.Group
	for i := range functions { 
		f := functions[i]  //請(qǐng)注意這里的寫法,下面有講解
		eg.Go(func() (err error) {
			err = f()
			if err != nil {
				//記日志
			}
			return err
		})
	}
	// 等待執(zhí)行完
	return eg.Wait()
}

上面就是使用ErrGroup庫(kù)的并發(fā)執(zhí)行任務(wù)的方法,可以直接拿來(lái)使用,ErrGroup這是GO官方提供的一個(gè)同步擴(kuò)展庫(kù),可以很好地將?個(gè)通?的?任務(wù)拆成?個(gè)?任務(wù)并發(fā)執(zhí)?

上面有一點(diǎn)需要特別注意的寫法,就是下面這段代碼的寫法,寫法1:

for i := range functions { 
		f := functions[i]  
		eg.Go(func() (err error) {
			err = f()

也可以這樣寫,寫法2:

for _, f := range functions { 
		fs := f  
		eg.Go(func() (err error) {
			err = fs()

但如果這樣寫就會(huì)有問(wèn)題,寫法3:

for _, f := range functions { 
		eg.Go(func() (err error) {
			err = f()

你們可以改一下,實(shí)際跑一下。會(huì)發(fā)現(xiàn) (寫法3) 會(huì)出現(xiàn)類似這樣的錯(cuò)誤結(jié)果

正確預(yù)期的結(jié)果(寫法1、寫法2)應(yīng)該是這樣的

這是因?yàn)樵?Go 語(yǔ)言中,當(dāng)使用閉包(匿名函數(shù))時(shí),如果閉包引用了外部的變量,閉包實(shí)際上會(huì)捕獲這些變量的引用。在循環(huán)中創(chuàng)建閉包時(shí),如果直接將循環(huán)變量作為閉包的參數(shù)或在閉包中引用該變量,會(huì)導(dǎo)致所有生成的閉包都引用相同的變量,即最后一次迭代的值。

為了避免這個(gè)問(wèn)題,常見(jiàn)的做法是在循環(huán)內(nèi)部創(chuàng)建一個(gè)新的變量,將循環(huán)變量的值賦給這個(gè)新變量,然后在閉包中引用該新變量。這樣,每次循環(huán)迭代都會(huì)創(chuàng)建一個(gè)新的變量,閉包捕獲的是不同的變量引用,而不是相同變量的引用。

在給定的代碼中,fs := f 就是為了創(chuàng)建一個(gè)新的變量 f,并將循環(huán)變量 f 的值賦給它。這樣,在閉包中就可以安全地引用這個(gè)新變量 f,而不會(huì)受到循環(huán)迭代的影響。這個(gè)技巧非常有用,可以在循環(huán)中創(chuàng)建多個(gè)獨(dú)立的閉包,并確保它們捕獲的是預(yù)期的變量值,而不會(huì)受到循環(huán)迭代的干擾。

當(dāng)然,還有一些第三方庫(kù)也實(shí)現(xiàn)了上面的并發(fā)分組操作,大家感興趣的可以去GitHub上看看,但功能和實(shí)現(xiàn)基本都大同小異。以上就是GO并發(fā)的基礎(chǔ),將一個(gè)父任務(wù)拆分成多個(gè)子任務(wù)去執(zhí)行,提高程序的并發(fā)度,節(jié)省程序耗時(shí)。我們平時(shí)在工作中,兩種方法都可以直接拿來(lái)使用,可以說(shuō)這兩個(gè)GO并發(fā)方法幾乎貫穿了我的GO職業(yè)生涯,也是最基礎(chǔ)最實(shí)用的并發(fā)操作方法

到此這篇關(guān)于go語(yǔ)言如何解決并發(fā)問(wèn)題的文章就介紹到這了,更多相關(guān)go語(yǔ)言并發(fā)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 使用Gin框架處理表單數(shù)據(jù)的操作步驟

    使用Gin框架處理表單數(shù)據(jù)的操作步驟

    在 Web 應(yīng)用開(kāi)發(fā)中,表單是用戶與服務(wù)器交互的重要手段,Gin 框架對(duì)表單處理提供了高效便捷的支持,包括數(shù)據(jù)綁定、驗(yàn)證等功能,在本篇博客中,我們將詳細(xì)介紹如何使用 Gin 框架處理表單數(shù)據(jù),涵蓋基礎(chǔ)操作與進(jìn)階技巧,幫助初學(xué)者全面掌握表單功能,需要的朋友可以參考下
    2024-11-11
  • Go語(yǔ)言實(shí)現(xiàn)逐行讀取和寫入文件詳解

    Go語(yǔ)言實(shí)現(xiàn)逐行讀取和寫入文件詳解

    這篇文章主要介紹了如何使用go語(yǔ)言實(shí)現(xiàn)從輸入文件中讀取每行數(shù)據(jù),然后將每行字段組合成SQL插入腳本,然后逐行寫入另外一個(gè)空白文件中,有需要的可以參考下
    2024-01-01
  • Golang switch語(yǔ)句的具體使用

    Golang switch語(yǔ)句的具體使用

    switch 語(yǔ)句提供了一種簡(jiǎn)潔的方式來(lái)執(zhí)行多路分支選擇,本文主要介紹了Golang switch語(yǔ)句的具體使用,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-08-08
  • go使用Gin框架利用阿里云實(shí)現(xiàn)短信驗(yàn)證碼功能

    go使用Gin框架利用阿里云實(shí)現(xiàn)短信驗(yàn)證碼功能

    這篇文章主要介紹了go使用Gin框架利用阿里云實(shí)現(xiàn)短信驗(yàn)證碼,使用json配置文件及配置文件解析,編寫路由controller層,本文通過(guò)代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2021-08-08
  • 詳解Go語(yǔ)言中切片的長(zhǎng)度與容量的區(qū)別

    詳解Go語(yǔ)言中切片的長(zhǎng)度與容量的區(qū)別

    切片可以看成是數(shù)組的引用,切片的長(zhǎng)度是它所包含的元素個(gè)數(shù)。切片的容量是從它的第一個(gè)元素到其底層數(shù)組元素末尾的個(gè)數(shù)。本文將通過(guò)示例詳細(xì)講講Go語(yǔ)言中切片的長(zhǎng)度與容量的區(qū)別,需要的可以參考一下
    2022-11-11
  • GO語(yǔ)言類型查詢類型斷言示例解析

    GO語(yǔ)言類型查詢類型斷言示例解析

    這篇文章主要為大家介紹了GO語(yǔ)言類型判斷及類型斷言,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2022-04-04
  • golang?cache帶索引超時(shí)緩存庫(kù)實(shí)戰(zhàn)示例

    golang?cache帶索引超時(shí)緩存庫(kù)實(shí)戰(zhàn)示例

    這篇文章主要為大家介紹了golang?cache帶索引超時(shí)緩存庫(kù)實(shí)戰(zhàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • 深入Go goroutine理解

    深入Go goroutine理解

    這篇文章主要介紹了深入Go goroutine理解,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-02-02
  • Go Plugins插件的實(shí)現(xiàn)方式

    Go Plugins插件的實(shí)現(xiàn)方式

    目前 Plugins 僅在 Linux、FreeBSD 和 macOS 上受支持,且只支持 golang 調(diào)用,今天通過(guò)本文給大家介紹Go Plugins插件的實(shí)現(xiàn)方式,感興趣的朋友一起看看吧
    2021-08-08
  • golang解析yaml文件操作

    golang解析yaml文件操作

    這篇文章主要介紹了golang解析yaml文件操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-12-12

最新評(píng)論