第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

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

1.Go 語言的 goroutine

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

代碼示例:

package main

import (
    "fmt"
    "time"
)

func main() {
    //打印0到10的數字
    go print0to10()
    //打印A到Z的字符
    go func() {
        for i := 'A'; i <= 'K'; i++ {
            fmt.Println("printAtoK:", string(i))
            time.Sleep(time.Microsecond)
        }
    }()
    time.Sleep(time.Second)
}

func print0to10() {
    for i := 0; i <= 10; i++ {
        fmt.Println("print0to10:", i)
        time.Sleep(time.Microsecond)
    }
}
  • 第 10 行:創(chuàng)建一個打印0到10數字的函數的 goroutine;
  • 第 11 行:使用匿名函數的方式創(chuàng)建一個打印A到Z的字符的 goroutine;
  • 第 15 和第 24 行:運行等待,讓出執(zhí)行資源給其它 goroutine;
  • 第 18 行:main 函數也是一個 goroutine,在它執(zhí)行結束后系統(tǒng)會殺掉在這個 goroutine 中執(zhí)行的所有goroutine ,所以要在 main 函數中加一個等待,為其內部的 goroutine 留出執(zhí)行時間。

執(zhí)行結果:

圖片描述

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

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

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

代碼示例:

package main

import (
    "fmt"
    "time"
)

var c1 chan rune = make(chan rune, 0)
var c2 chan int = make(chan int, 0)

func main() {
    //打印0到10的數字
    go print0to10()
    //打印A到Z的字符
    go func() {
        c2 <- 0
        for i := 1; i <= 11; i++ {
            char := <-c1
            fmt.Println("printAtoK:", string(char))
            c2 <- i
        }
    }()
    time.Sleep(time.Second)
}

func print0to10() {
    for i := 'A'; i <= 'K'; i++ {
        num := <-c2
        fmt.Println("print0to10:", num)
        c1 <- i
    }
}

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

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

執(zhí)行結果:

圖片描述

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

3. Go語言進程鎖

在之前介紹 map 的小節(jié)中提到過線程不安全的 map 。之所以線程不安全是因為其內部實現機制中無法同時讀寫,若有兩個 goroutine 一個在讀取 map 中的值,而另一個在更新 map 中的值,就會導致程序崩潰。

代碼示例:

package main

import (
    "fmt"
    "time"
)

func main() {
    m := map[string]int{"A": 1, "B": 2, "C": 3, "D": 1, "E": 2, "F": 3}
    //創(chuàng)建100個goroutine對map進行讀寫
    for i := 0; i < 100; i++ {
        go func() {
            for v := range m {
                m[v] = 100
            }
        }()
    }
    time.Sleep(time.Second)
    fmt.Println(m)
}

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

圖片描述

但更多的時候會輸出讀寫沖突的錯誤:

圖片描述

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

代碼示例:

package main

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

func main() {
    var lock sync.Mutex//定義一個鎖變量
    m := map[string]int{"A": 1, "B": 2, "C": 3, "D": 1, "E": 2, "F": 3}
    for i := 0; i < 100; i++ {
        go func() {
            lock.Lock()//在讀取map前鎖定這個鎖,使其它線程訪問這個鎖要阻塞
            for v := range m {
                m[v] = 100
            }
            lock.Unlock()//在讀取map前釋放這個鎖
        }()
    }
    time.Sleep(time.Second)
    fmt.Println(m)
}

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

圖片描述

4. 小結

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

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