激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

腳本之家,腳本語言編程技術及教程分享平臺!
分類導航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務器之家 - 腳本之家 - Golang - 詳解Go內存模型

詳解Go內存模型

2021-02-22 00:48alenliu0621 Golang

這篇文章主要介紹了Go 內存模型的相關資料,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下

介紹

Go 內存模型規定了一些條件,在這些條件下,在一個 goroutine 中讀取變量返回的值能夠確保是另一個 goroutine 中對該變量寫入的值。【翻譯這篇文章花費了我 3 個半小時 】

Happens Before(在…之前發生)

在一個 goroutine 中,讀操作和寫操作必須表現地就好像它們是按照程序中指定的順序執行的。這是因為,在一個 goroutine 中編譯器和處理器可能重新安排讀和寫操作的執行順序(只要這種亂序執行不改變這個 goroutine 中在語言規范中定義的行為)。

因為亂序執行的存在,一個 goroutine 觀察到的執行順序可能與另一個 goroutine 觀察到的執行順序不同。 比如,如果一個 goroutine 執行a = 1; b = 2;,另一個 goroutine 可能觀察到 b 的值在 a 之前更新。

為了規定讀取和寫入的必要條件,我們定義了 happens before (在…之前發生),一個在 Go 程序中執行內存操作的部分順序。如果事件 e1 發生在事件 e2 之前,那么我們說 e2 發生在 e1 之后。同樣,如果 e1 不在 e2 之前發生也不在 e2 之后發生,那么我們說 e1 和 e2 同時發生。

在一個單獨的 goroutine 中,happens-before 順序就是在程序中的順序。

一個對變量 v 的 讀操作 r 可以被允許觀察到一個對 v 的寫操作 w,如果下列條件同時滿足:

r 不在 w 之前發生在 w 之后,r 之前,沒有其他對 v 的寫入操作 w' 發生。

為了確保一個對變量 v 的讀操作 r 觀察到一個對 v 的 寫操作 w,必須確保 w 是唯一的 r 允許的寫操作。就是說下列條件必須同時滿足:

w 在 r 之前發生任何其他對共享的變量 v 的寫操作發生在 w 之前或 r 之后。

這兩個條件比前面兩個條件要嚴格,它要求不能有另外的寫操作與 w 或 r 同時發生。

在一個單獨的 goroutine 中,沒有并發存在,所以這兩種定義是等價的:一個讀操作 r 觀察到的是最近對 v 的寫入操作 w 。當多個 goroutine 訪問一個共享的變量 v 時,它們必須使用同步的事件來建立 happens-before 條件來確保讀操作觀察到預期的寫操作。

在內存模型中,使用零值初始化一個變量的 v 的行為和寫操作的行為一樣。

讀取和寫入超過單個機器字【32 位或 64 位】大小的值的行為和多個無序地操作單個機器字的行為一樣。

同步

初始化

程序初始化操作在一個單獨的 goroutine 中運行,但是這個 goroutine 可能創建其他并發執行的 goroutines。

如果包 p 導入了包 q,那么 q 的 init 函數執行完成發生在 p 的任何 init 函數執行之前。

函數 main.main【也就是 main 函數】 的執行發生在所有的 init 函數完成之后。

Goroutine 創建

啟動一個新的 goroutine 的 go 語句的執行在這個 goroutine 開始執行前發生。

比如,在這個程序中:

?
1
2
3
4
5
6
7
8
9
10
var a string
 
func f() {
    print(a) //
}
 
func hello() {
    a = "hello, world"
    go f() //
}

調用 hello 函數將會在之后的某個事件點打印出 “hello, world”。【因為 a = “hello, world” 語句在 go f() 語句之前執行,而 goroutine 執行的函數 f 在 go f() 語句之后執行,a 的值已經初始化了 】

Goroutine 銷毀

goroutine 的退出不保證發生在程序中的任何事件之前。比如,在這個程序中:

?
1
2
3
4
5
6
var a string
 
func hello() {
    go func() { a = "hello" }()
    print(a)
}

a 的賦值之后沒有跟隨任何同步事件,所以不能保證其他的 goroutine 能夠觀察到賦值操作。事實上,一個激進的編譯器可能刪除掉整個 go 語句。

如果在一個 goroutine 中賦值的效果必須被另一個 goroutine 觀察到,那么使用鎖或者管道通信這樣的同步機制來建立一個相對的順序。

管道通信

管道通信是在 goroutine 間同步的主要方法。一個管道的發送操作匹配【對應】一個管道的接收操作(通常在另一個 goroutine 中)。

一個在有緩沖的管道上的發送操作在相應的接收操作完成之前發生。

這個程序:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
var c = make(chan int, 10) // 有緩沖的管道
var a string
 
func f() {
    a = "hello, world"
    c <- 0 // 發送操作,先
}
 
func main() {
    go f()
    <-c // 接收操作,后
    print(a)
}

能夠確保輸出 “hello, world”。因為對 a 的賦值操作在發送操作前完成,而接收操作在發送操作之后完成。

關閉一個管道發生在從管道接收一個零值之前。

在之前的例子中,將 c <- 0 語句替換成 close(c) 效果是一樣的。

一個在無緩沖的管道上的接收操作在相應的發送操作完成之前發生。

這個程序 (和上面一樣,使用無緩沖的管道,調換了發送和接收操作):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
var c = make(chan int) // 無緩沖的管道
var a string
 
func f() {
    a = "hello, world"
    <-c // 接收操作,先
}
 
func main() {
    go f()
    c <- 0 // 發送操作,后
    print(a)
}

也會確保輸出 “hello, world”。

如果管道是由緩沖的 (比如, c = make(chan int, 1))那么程序不能夠確保輸出 "hello, world". (它可能會打印出空字符串、或者崩潰、或者做其他的事)

在一個容量為 C 的管道上的第 k 個接收操作在第 k+C 個發送操作完成之前發生。

該規則將前一個規則推廣到帶緩沖的管道。它允許使用帶緩沖的管道實現計數信號量模型:管道中的元素數量對應于正在被使用的數量【信號量的計數】,管道的容量對應于同時使用的最大數量,發送一個元素獲取信號量,接收一個元素釋放信號量。這是一個限制并發的常見用法。

下面的程序對工作列表中的每一項啟動一個 goroutine 處理,但是使用 limit 管道來確保同一時間內只有 3 個工作函數在運行。

?
1
2
3
4
5
6
7
8
9
10
11
12
var limit = make(chan int, 3)
 
func main() {
    for _, w := range work {
        go func(w func()) {
            limit <- 1 // 獲取信號量
            w()
            <-limit // 釋放信號量
        }(w)
    }
    select{}
}

sync 包實現了兩個鎖數據類型,sync.Mutexsync.RWMutex

對任何 sync.Mutexsync.RWMutex 類型的變量 ln < m,第 n 個l.Unlock()操作在第 m 個 l.Lock() 操作返回之前發生。

這個程序:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var l sync.Mutex
var a string
 
func f() {
    a = "hello, world"
    l.Unlock() // 第一個 Unlock 操作,先
}
 
func main() {
    l.Lock()
    go f()
    l.Lock() // 第二個 Lock 操作,后
    print(a)
}

保證會打印出"hello, world"

Once

sync 包提供了 Once 類型,為存在多個 goroutine 時的初始化提供了一種安全的機制。多個線程可以為特定的 f 執行一次 once.Do(f),但是只有一個會運行 f(),其他的調用將會阻塞直到 f() 返回。

一個從 once.Do(f) 調用的 f()的返回在任何 once.Do(f) 返回之前發生。

在這個程序中:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var a string
var once sync.Once
 
func setup() {
    a = "hello, world" // 先
}
 
func doprint() {
    once.Do(setup)
    print(a) // 后
}
 
func twoprint() {
    go doprint()
    go doprint()
}

調用 twoprint 只會調用 setup 一次。setup 函數在調用 print 函數之前完成。結果將會打印兩次"hello, world"。

不正確的同步

注意到一個讀操作 r 可能觀察到與它同時發生的寫操作w 寫入的值。當這種情況發生時,那也不能確保在 r 之后發生的讀操作能夠觀察到在 w 之前發生的寫操作。

在這個程序中:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var a, b int
 
func f() {
    a = 1
    b = 2
}
 
func g() {
    print(b)
    print(a)
}
 
func main() {
    go f()
    g()
}

可能會發生函數 g 輸出 2 然后 0 的情況。【b 的值輸出為2,說明已經觀察到了 b 的寫入操作。但是之后讀取 a 的值卻為 0,說明沒有觀察到 b 寫入之前的 a 寫入操作!不能以為 b 的值是 2,那么 a 的值就一定是 1 !】

這個事實使一些常見的處理邏輯無效。

比如,為了避免鎖帶來的開銷,twoprint 那個程序可能會被不正確地寫成:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var a string
var done bool
 
func setup() {
    a = "hello, world"
    done = true
}
 
func doprint() {
    if !done { // 不正確!
        once.Do(setup)
    }
    print(a)
}
 
func twoprint() {
    go doprint()
    go doprint()
}

這樣寫不能保證在 doprint 中觀察到了對 done 的寫入。這個版本可能會不正確地輸出空串。

另一個不正確的代碼邏輯是循環等待一個值改變:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var a string
var done bool
 
func setup() {
    a = "hello, world"
    done = true
}
 
func main() {
    go setup()
    for !done { // 不正確!
    }
    print(a)
}

和之前一樣,在 main 中,觀察到了對 done 的寫入并不意味著觀察到了對 a 的寫入,所以這個程序可能也會打印一個空串。更糟糕的是,不能夠保證對 done 的寫入會被 main 觀察到,因為兩個線程間沒有同步事件。 在 main 中的循環不能確保會完成。

類似的程序如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
type T struct {
    msg string
}
 
var g *T
 
func setup() {
    t := new(T)
    t.msg = "hello, world"
    g = t
}
 
func main() {
    go setup()
    for g == nil { // 不正確
    }
    print(g.msg)
}

即使 main 觀察到了 g != nil,退出了循環,也不能確保它觀察到了 g.msg 的初始值。

在所有這些例子中,解決方法都是相同的:使用顯示地同步。

到此這篇關于Go 內存模型的文章就介紹到這了,更多相關Go 內存模型內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://blog.csdn.net/woay2008/article/details/110748677

延伸 · 閱讀

精彩推薦
  • GolangGolang中Bit數組的實現方式

    Golang中Bit數組的實現方式

    這篇文章主要介紹了Golang中Bit數組的實現方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    天易獨尊11682021-06-09
  • GolangGolang通脈之數據類型詳情

    Golang通脈之數據類型詳情

    這篇文章主要介紹了Golang通脈之數據類型,在編程語言中標識符就是定義的具有某種意義的詞,比如變量名、常量名、函數名等等,Go語言中標識符允許由...

    4272021-11-24
  • Golanggo日志系統logrus顯示文件和行號的操作

    go日志系統logrus顯示文件和行號的操作

    這篇文章主要介紹了go日志系統logrus顯示文件和行號的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    SmallQinYan12302021-02-02
  • Golanggolang 通過ssh代理連接mysql的操作

    golang 通過ssh代理連接mysql的操作

    這篇文章主要介紹了golang 通過ssh代理連接mysql的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    a165861639710342021-03-08
  • Golanggolang的httpserver優雅重啟方法詳解

    golang的httpserver優雅重啟方法詳解

    這篇文章主要給大家介紹了關于golang的httpserver優雅重啟的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,...

    helight2992020-05-14
  • Golanggolang如何使用struct的tag屬性的詳細介紹

    golang如何使用struct的tag屬性的詳細介紹

    這篇文章主要介紹了golang如何使用struct的tag屬性的詳細介紹,從例子說起,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看...

    Go語言中文網11352020-05-21
  • Golanggo語言制作端口掃描器

    go語言制作端口掃描器

    本文給大家分享的是使用go語言編寫的TCP端口掃描器,可以選擇IP范圍,掃描的端口,以及多線程,有需要的小伙伴可以參考下。 ...

    腳本之家3642020-04-25
  • Golanggolang json.Marshal 特殊html字符被轉義的解決方法

    golang json.Marshal 特殊html字符被轉義的解決方法

    今天小編就為大家分享一篇golang json.Marshal 特殊html字符被轉義的解決方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧 ...

    李浩的life12792020-05-27
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25
主站蜘蛛池模板: 久久国产精品二国产精品中国洋人 | 欧美特级黄色 | 俄罗斯hdxxx 日夜操天天干 | 国产欧美日韩在线播放 | 欧美曾交 | 成人短视频在线播放 | 亚洲男人一区 | 免费a级毛片永久免费 | 中文字幕在线一 | 午夜国产福利 | 国产妇女乱码一区二区三区 | 亚洲性夜色噜噜噜7777 | 久久久入口 | 国产精品久久久久久久久久了 | 久久电影一区二区 | 久久96国产精品久久久 | 国内成人自拍视频 | 欧美片a | 亚洲精品有限 | 欧美一级xxx | 免费毛片视频播放 | 操操插插| 欧美一级无毛 | 国产毛片网站 | 国产成人高清在线 | av观看国产| 日本在线播放一区二区 | 最近免费中文字幕在线视频2 | 久久国产精品小视频 | 午夜人体 | 欧洲成人综合网 | 中文字幕在线播放一区 | 2级毛片| 毛片免费在线观看 | av色偷偷 | 在线播放免费播放av片 | 91成人免费看片 | 久久国产乱子伦精品 | 在线免费观看麻豆 | 免费看日韩av | 黄色特级片黄色特级片 |