<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          go高并發(fā)之路——go語言如何解決并發(fā)問題

          共 4145字,需瀏覽 9分鐘

           ·

          2024-06-14 17:52

          一、選擇GO的原因

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

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

          2、GO語言當(dāng)時在市面上很火,像騰訊、百度、滴滴、好未來這些大廠都在陸陸續(xù)續(xù)地從PHP轉(zhuǎn)向GO,這也是一個訊號吧,跟著大佬們走總不會錯。

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

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

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

          但如果換成GO去做這件事,那就非常清爽了,這個用戶請求耗時就只需要時間最長的那個操作耗時,如下圖:

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

          方法1:使用sync.WaitGroup

          //請求入口
          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í)行第一個任務(wù)")
          }, func() {
          LiveDetail = 2 //直播基礎(chǔ)信息
          time.Sleep(2 * time.Second)
          fmt.Println("執(zhí)行第二個任務(wù)")
          }, func() {
          UserDetail = 3 //用戶信息
          time.Sleep(3 * time.Second)
          fmt.Println("執(zhí)行第三個任務(wù)")
          }, func() {
          EquityDetail = 4 //直播關(guān)聯(lián)權(quán)益信息
          time.Sleep(4 * time.Second)
          fmt.Println("執(zhí)行第四個任務(wù)")
          }, func() {
          StatisticsDetail = 5 //直播間信息統(tǒng)計
          time.Sleep(5 * time.Second)
          fmt.Println("執(zhí)行第五個任務(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)
          // 每個函數(shù)啟動一個協(xié)程
          go func(function func()) {
          function()
          wg.Done()
          }(f)
          }
          // 等待執(zhí)行完
          wg.Wait()
          }

          方法2:使用ErrGroup庫

          //請求入口
          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í)行第一個任務(wù)")
          return nil //返回實際執(zhí)行的錯誤
          }, func() error {
          LiveDetail = 2 //直播基礎(chǔ)信息
          time.Sleep(2 * time.Second)
          fmt.Println("執(zhí)行第二個任務(wù)")
          return nil //返回實際執(zhí)行的錯誤
          }, func() error {
          UserDetail = 3 //用戶信息
          time.Sleep(3 * time.Second)
          fmt.Println("執(zhí)行第三個任務(wù)")
          return nil //返回實際執(zhí)行的錯誤
          }, func() error {
          EquityDetail = 4 //直播關(guān)聯(lián)權(quán)益信息
          time.Sleep(4 * time.Second)
          fmt.Println("執(zhí)行第四個任務(wù)")
          return nil //返回實際執(zhí)行的錯誤
          }, func() error {
          StatisticsDetail = 5 //直播間信息統(tǒng)計
          time.Sleep(5 * time.Second)
          fmt.Println("執(zhí)行第五個任務(wù)")
          return nil //返回實際執(zhí)行的錯誤
          })
          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] //請注意這里的寫法,下面有講解
          eg.Go(func() (err error) {
          err = f()
          if err != nil {
          //記日志
          }
          return err
          })
          }
          // 等待執(zhí)行完
          return eg.Wait()
          }

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

          上面有一點需要特別注意的寫法,就是下面這段代碼的寫法,寫法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()

          但如果這樣寫就會有問題,寫法3:

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

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

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

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

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

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

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

          一個人可以被毀滅,但不可以被打敗。

          鏈接:https://www.cnblogs.com/lmz-blogs/p/18200946

          (版權(quán)歸原作者所有,侵刪)


          瀏覽 68
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本a在线免费观看 | 免费A片视频在线观看 | 亚洲色情网站 | 日韩中文字幕在线观看视频 | 亚洲最大性爱网站 |