<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ā)編程

          共 3137字,需瀏覽 7分鐘

           ·

          2022-10-20 16:32

          d01b45ab2639ebc89e4ce2de292deaad.webp

          Go 語言在創(chuàng)建之初,CPU 多核發(fā)展正猛,Go 語言的創(chuàng)始人果斷將面向多核、原生支持并發(fā)作為了 Go 語言的設計目標之一,所以在 Go 語言中使用并發(fā)有得天獨厚的優(yōu)勢。

          那么,什么是并發(fā)呢?

          聊到并發(fā),就會有一系列的其他概念相繼而來,比如:并行、進程、線程、異步等。

          我們經常使用 C# 的 Winform 程序寫一些工具,編譯后為一個 exe 文件,文件執(zhí)行后就會作為一個進程在 Windows 中執(zhí)行,在之前的單核 CPU 時代,在某個時刻只能執(zhí)行一個進程對應的程序代碼,兩個進程不存在并行執(zhí)行的可能,多個處理器或多核處理器是并行執(zhí)行的必要條件。

          如果對程序進行改造,對執(zhí)行的任務進行分解,每個分解出來的小的模塊由一個單獨的線程進行處理,多個線程共享這個進程所擁有的資源,線程作為執(zhí)行單元可被獨立調度到處理器上運行。也許還是執(zhí)行在單 CPU 中,但是在并發(fā)執(zhí)行的。

          Go 語言的創(chuàng)始人 Rob Pike 曾說過:并行關乎執(zhí)行,并發(fā)關乎結構。

          舉一個生活中的例子:

          現(xiàn)在每天都在做的排隊做核酸,有三個步驟:

          1、排隊;

          2、掃二維碼;

          3、捅喉嚨

          4e31b7daa72e1b2482146de433d93276.webp


          • 需要做核酸的人分多個隊伍進行排隊,多個隊伍在并行進行處理;
          • 每一個隊伍中只有一個檢測人員,先進行掃碼,然后去捅喉嚨,兩個步驟完成后,再進行下一個。


          550fc4760f06e21a810dd202b40c4259.webp


          • 掃碼和捅喉嚨都有單獨的人員進行處理,這就像將程序拆分成多個線程進行處理一樣;
          • 并發(fā)在單 CPU 也能發(fā)生,就像上圖中只有一個隊伍的情況,但多核或多 CPU 能發(fā)揮更大的作用。

          由此可見,要發(fā)揮并發(fā)的作用,離不開線程和多核,線程創(chuàng)建的成本雖然已經比進程小了很多,但依然不適合大規(guī)模地創(chuàng)建線程,因為除了每個線程占用的資源外,操作系統(tǒng)調度線程的成本也不小。

          因此,Go 語言創(chuàng)造了 goroutine ,也叫協(xié)程,這是一個由 Go 運行時負責調度的輕量級線程。和常規(guī)的線程相比,有這些好處:

          • 資源占用小, goroutine 的 Stack 的初始化的大小為 2k ,而像 C# 、Java 語言中線程的 Stack 都是兆級別的,所以 goroutine 的創(chuàng)建會更加快;
          • goroutine 是由 Go 運行時調度,而不是操作系統(tǒng),切換速度會更快。

          在 Go 中怎樣使用 goroutine 呢?非常的簡單,使用關鍵字 go 就可以了,默認情況下,主程序在單獨的一個 goroutine 中,如果某個函數(shù)或匿名函數(shù)使用了 go 關鍵字,那么就會創(chuàng)建一個單獨的 goroutine 。

                
                package?main

          import?(
          ?"fmt"
          ?"time"
          )

          func?testGouroutine(name?string)?{
          ?fmt.Println("goroutine:",?name)
          }
          func?main()?{
          ?fmt.Println("這是主程序")
          ?go?testGouroutine("1")
          ?go?testGouroutine("2")
          ?go?testGouroutine("3")
          ?go?testGouroutine("4")
          ?time.Sleep(time.Second)
          }

          如果在使用 go 關鍵字的函數(shù)中使用了主程序中的資源,就會出現(xiàn)競爭的情況,看下面的這個例子:

                
                package?main

          import?(
          ?"fmt"
          ?"time"
          )

          func?test()?{
          ?counter?:=?0
          ?for?i?:=?0;?i?<?5000;?i++?{
          ??go?func()?{
          ???counter++
          ??}()
          ?}
          ?time.Sleep(time.Second)
          ?fmt.Println("counter:",?counter)
          }

          func?main()?{
          ?test()
          }

          每次運行,counter 的值都會不一樣,因為每次不同的協(xié)程對公共資源 counter 的搶奪情況不一樣,要解決這個問題就要用到鎖:

                
                package?main

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

          func?test()?{
          ?var?mut?sync.Mutex

          ?counter?:=?0
          ?for?i?:=?0;?i?<?5000;?i++?{
          ??go?func()?{
          ???defer?mut.Unlock()
          ???mut.Lock()
          ???counter++
          ??}()
          ?}
          ?time.Sleep(time.Second)

          ?fmt.Println("counter:",?counter)
          }

          func?main()?{
          ?test()
          }

          值得注意的是,使用 go 關鍵字執(zhí)行的函數(shù)即便是有返回值,也會被忽略,如果需要在 goroutine 之間進行通信,需要使用通道。

          通道使用 make 進行構建,關鍵字為 chan 。看下面的例子:

                
                package?main

          import?(
          ?"fmt"
          ?"strconv"
          ?"time"
          )

          func?main()?{
          ?//使用?make?創(chuàng)建一個字符型的通道,通道使用關鍵字?chan
          ?msg?:=?make(chan?string)
          ?//在異步匿名函數(shù)中模擬同步數(shù)據(jù),完成一個給通道發(fā)送一個消息
          ?go?func()?{
          ??for?i?:=?1;?i?<=?10;?i++?{
          ???if?i?==?10?{
          ????msg?<-?"success"
          ???}?else?{
          ????msg?<-?"總共需要同步?10?個模塊,已經處理?"?+?strconv.Itoa(i)?+?"個"
          ???}
          ???//每循環(huán)依次,演示?1?秒,模擬耗時
          ???time.Sleep(time.Second)
          ??}
          ?}()

          ?//主線程中進行進度顯示
          ?for?m?:=?range?msg?{
          ??if?m?==?"success"?{
          ???fmt.Println("同步完成")
          ???break
          ??}?else?{
          ???fmt.Println(m)
          ??}
          ?}
          }

          在匿名函數(shù)中每隔一秒就給主 gorourine 發(fā)送一個消息,主 gorourine 把這個消息打印出來。

          最后總結下:

          1、并行關乎執(zhí)行,并發(fā)關乎結構;

          2、Go 語言的并發(fā)是基于輕量級的 goroutine ,相比普通的線程,goroutine 有很多的好處;

          3、在不同的 goroutine 之間進行通信需要用到通道,通道使用 make 創(chuàng)建,關鍵字為 chan 。

          相關閱讀:

          Go 學習:從環(huán)境搭建到寫一個 Web 服務

          Go 學習:那些不一樣的知識點(上)

          Go 學習:那些不一樣的知識點(下)

          系統(tǒng)學習還是得看書,Go 書籍推薦


          瀏覽 32
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  WW青青草 | 国产又大又粗又黄 | 麻豆成人91精品二区三区 | 久热精品视频在线播放 | 免费手机在线看A片 |