亚洲熟女综合色一区二区三区,亚洲精品中文字幕无码蜜桃,亚洲va欧美va日韩va成人网,亚洲av无码国产一区二区三区,亚洲精品无码久久久久久久

Go 語言的并發(fā)

Go 語言中的多線程操作是其語言的一大特色,它具有其它語言無法比擬的,可以近乎無限開啟的線程。在 Go 語言中被稱之為?goroutine?,它是線程的輕量級(jí)實(shí)現(xiàn)。Go 語言的并發(fā)廣泛的應(yīng)用在服務(wù)器性能調(diào)優(yōu)的場(chǎng)景中,這也是越來越多的游戲服務(wù)器開發(fā)都在往 Go 語言傾斜的原因之一。

 

1.Go 語言的 goroutine

在 Go 語言中使用?go?關(guān)鍵字來創(chuàng)建 goroutine ,形如go 函數(shù)名()的形式去創(chuàng)建。每一個(gè) goroutine 必須是一個(gè)函數(shù),這個(gè)函數(shù)也可以是匿名函數(shù)。

代碼示例:

代碼塊
  • 1? package main
  • 2
  • 3? import (
  • 4? ? ? ? "fmt"
  • 5? ? ? ? "time"
  • 6? )
  • 7
  • 8? func main() {
  • 9? ? ? ? //打印0到10的數(shù)字
  • 10? ? ? go print0to10()
  • 11? ? ? //打印A到Z的字符
  • 12? ? ? go func() {
  • 13? ? ? ? ? ? ? for i := 'A'; i <= 'K'; i++ {
  • 14? ? ? ? ? ? ? ? ? ? ? fmt.Println("printAtoK:", string(i))
  • 15? ? ? ? ? ? ? ? ? ? ? time.Sleep(time.Microsecond)
  • 16? ? ? ? ? ? ? }
  • 17? ? ? ? }()
  • 18? ? ? ? time.Sleep(time.Second)
  • 19? }
  • 20
  • 21? func print0to10() {
  • 22? ? ? ? ? ? for i := 0; i <= 10; i++ {
  • 23? ? ? ? ? ? ? ? ? ? fmt.Println("print0to10:", i)
  • 24? ? ? ? ? ? ? ? ? ? time.Sleep(time.Microsecond)
  • 25? ? ? ? ? ? }
  • 26? }
  • 第 10 行:創(chuàng)建一個(gè)打印0到10數(shù)字的函數(shù)的 goroutine;
  • 第 11 行:使用匿名函數(shù)的方式創(chuàng)建一個(gè)打印A到Z的字符的 goroutine;
  • 第 15 和第 24 行:運(yùn)行等待,讓出執(zhí)行資源給其它 goroutine;
  • 第 18 行:main 函數(shù)也是一個(gè) goroutine,在它執(zhí)行結(jié)束后系統(tǒng)會(huì)殺掉在這個(gè) goroutine 中執(zhí)行的所有g(shù)oroutine?,所以要在 main 函數(shù)中加一個(gè)等待,為其內(nèi)部的 goroutine 留出執(zhí)行時(shí)間。

執(zhí)行結(jié)果:

圖片描述

從執(zhí)行結(jié)果中可以看出打印數(shù)字和打印字符的兩個(gè) goroutine 是并發(fā)執(zhí)行的。執(zhí)行順序是由 cpu 來調(diào)度的,所以執(zhí)行結(jié)果可能每次都不一樣。

 

2. Go語言并發(fā)通訊

其它語言并發(fā)時(shí)進(jìn)程中的通訊一般都是通過共享內(nèi)存(全局變量)的方式來實(shí)現(xiàn)的,這樣一來各個(gè)模塊之間的耦合會(huì)變得非常緊密。所以后來提出了使用通訊來共享內(nèi)存這一概念,來解耦合。在 Go 語言中就是使用 channel 的方式來達(dá)到這一目的的。

代碼示例:

代碼塊
  • 1? package main
  • 2
  • 3? import (
  • 4? ? ? ? ? "fmt"
  • 5? ? ? ? ? "time"
  • 6? )
  • 7
  • 8? var c1 chan rune = make(chan rune, 0)
  • 9? var c2 chan int = make(chan int, 0)
  • 10
  • 11? func main() {
  • 12? ? ? ? ? //打印0到10的數(shù)字
  • 13? ? ? ? ? go print0to10()
  • 14? ? ? ? ? //打印A到Z的字符
  • 15? ? ? ? ?? go func() {
  • 16? ? ? ? ? ? ? ? ? ? c2 <- 0
  • 17? ? ? ? ? ? ? ? ? ? for i := 1; i <= 11; i++ {
  • 18? ? ? ? ? ? ? ? ? ? ? ? ? ? char := <-c1
  • 19? ? ? ? ? ? ? ? ? ? ? ? ? ? fmt.Println("printAtoK:", string(char))
  • 20? ? ? ? ? ? ? ? ? ? ? ? ? ? c2 <- i
  • 21? ? ? ? ? ? ? ? ? ? }
  • 22? ? ? ? ? ? }()
  • 23? ? ? ? ? ? time.Sleep(time.Second)
  • 24? }
  • 25
  • 26? func print0to10() {
  • 27? ? ? ? ? ? for i := 'A'; i <= 'K'; i++ {
  • 28? ? ? ? ? ? ? ? ?? num := <-c2
  • 29? ? ? ? ? ? ? ? ? ?fmt.Println("print0to10:", num)
  • 30? ? ? ? ? ? ? ? ? ?c1 <- i
  • 31? ? ? ? ? ? }
  • 32? }

上述代碼主要實(shí)現(xiàn)的功能為,使用兩個(gè)通道來使兩個(gè) goroutine 互相通訊,從而使得它們的打印安裝輪流打印的方式打印數(shù)字和字母。

  • 第 8 行:實(shí)例化一個(gè)字符通道用于接收字符;
  • 第 9 行:實(shí)例化一個(gè)數(shù)字通道用于接收數(shù)字;
  • 第 16 行:向數(shù)字通道中塞入數(shù)字0,用于觸發(fā)打印數(shù)字的 goroutine;
  • 第 18 行:從字符通道中獲取一個(gè)待打印的字符。若通道中無字符,則阻塞等待;
  • 第 20 行:字符打印完畢之后再向數(shù)字通道中塞入后續(xù)數(shù)字,觸發(fā)打印數(shù)字的 goroutine;
  • 第 28 行:從數(shù)字通道中獲取待打印的數(shù)字,若通道中無數(shù)字,則阻塞等待;
  • 第 30 行:數(shù)字打印完畢之后再向字符通道中塞入后續(xù)字符,觸發(fā)打印字符的 goroutine。

執(zhí)行結(jié)果:

圖片描述

和沒用使用 channel 之前的代碼不同,這次等同于使用 channel 實(shí)現(xiàn)了 goroutine 的調(diào)度,使其輪流執(zhí)行。

 

3. Go語言進(jìn)程鎖

在之前介紹 map 的小節(jié)中提到過線程不安全的 map 。之所以線程不安全是因?yàn)槠鋬?nèi)部實(shí)現(xiàn)機(jī)制中無法同時(shí)讀寫,若有兩個(gè) goroutine?一個(gè)在讀取 map 中的值,而另一個(gè)在更新 map 中的值,就會(huì)導(dǎo)致程序崩潰。

代碼示例:

代碼塊
  • 1? package main
  • 2
  • 3? import (
  • 4? ? ? ? ? ? "fmt"
  • 5? ? ? ? ? ? "time"
  • 6? )
  • 7
  • 8? func main() {
  • 9? ? ? ? ? ? m := map[string]int{"A": 1, "B": 2, "C": 3, "D": 1, "E": 2, "F": 3}
  • 10? ? ? ? ? //創(chuàng)建100個(gè)goroutine對(duì)map進(jìn)行讀寫
  • 11? ? ? ? ? for i := 0; i < 100; i++ {
  • 12? ? ? ? ? ? ? ? ? ? go func() {
  • 13? ? ? ? ? ? ? ? ? ? ? ? ? ? ? for v := range m {
  • 14? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? m[v] = 100
  • 15? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
  • 16? ? ? ? ? ? ? ? ? ? }()
  • 17? ? ? ? ? }
  • 18? ? ? ? ? time.Sleep(time.Second)
  • 19? ? ? ? ? fmt.Println(m)
  • 20? }

執(zhí)行上述代碼有時(shí)會(huì)輸出正確結(jié)果:

圖片描述

但更多的時(shí)候會(huì)輸出讀寫沖突的錯(cuò)誤:

圖片描述

這個(gè)就是線程不安全的 map 不建議使用的原因,除了直接使用線程安全的 map 之外,還可以為這些 goruntine?加上鎖,使其無法同時(shí)對(duì) map 進(jìn)行讀寫操作,這樣也可以保障各線程的安全。

代碼示例:

代碼塊
  • 1? package main
  • 2
  • 3? import (
  • 4? ? ? ? ? ? "fmt"
  • 5? ? ? ? ? ? "sync"
  • 6? ? ? ? ? ? "time"
  • 7? )
  • 8
  • 9? func main() {
  • 10? ? ? ? ? var lock sync.Mutex//定義一個(gè)鎖變量
  • 11? ? ? ? ? m := map[string]int{"A": 1, "B": 2, "C": 3, "D": 1, "E": 2, "F": 3}
  • 12? ? ? ? ? for i := 0; i < 100; i++ {
  • 13? ? ? ? ? ? ? ? ? go func() {
  • 14? ? ? ? ? ? ? ? ? ? ? ? ? lock.Lock()//在讀取map前鎖定這個(gè)鎖,使其它線程訪問這個(gè)鎖要阻塞
  • 15? ? ? ? ? ? ? ? ? ? ? ? ? for v := range m {
  • 16? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? m[v] = 100
  • 17? ? ? ? ? ? ? ? ? ? ? ? ? }
  • 18? ? ? ? ? ? ? ? ? ? ? ? ? lock.Unlock()//在讀取map前釋放這個(gè)鎖
  • 19? ? ? ? ? ? ? ? ? }()
  • 20? ? ? ? ? }
  • 21? ? ? ? ? time.Sleep(time.Second)
  • 22? ? ? ? ? fmt.Println(m)
  • 23? }

加了鎖之后,你就會(huì)發(fā)現(xiàn)無論執(zhí)行幾次,執(zhí)行結(jié)果都是正確的。

圖片描述

 

4. 小結(jié)

本文主要介紹了Go語言中的多線程——goroutine。其實(shí)現(xiàn)是線程的輕量實(shí)現(xiàn),所以可以無限制的開啟。在使用過程中需要注意:

  • goroutine 執(zhí)行無先后順序,由 cpu 統(tǒng)一調(diào)度。
  • goroutine 之間內(nèi)存的共享通過使用 channel 來通訊實(shí)現(xiàn)。
  • goroutine 使用線程不安全的變量類型時(shí)可以用鎖將其鎖定。

文章來源于網(wǎng)絡(luò),侵刪!

相關(guān)新聞

歷經(jīng)多年發(fā)展,已成為國(guó)內(nèi)好評(píng)如潮的Linux云計(jì)算運(yùn)維、SRE、Devops、網(wǎng)絡(luò)安全、云原生、Go、Python開發(fā)專業(yè)人才培訓(xùn)機(jī)構(gòu)!