一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

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

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

服務器之家 - 腳本之家 - Golang - 淺談golang并發操作變量安全的問題

淺談golang并發操作變量安全的問題

2021-03-17 00:56思維的深度 Golang

這篇文章主要介紹了淺談golang并發操作變量安全的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧

我就廢話不多說了,大家還是直接看代碼吧~

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package main
import (
    "fmt"
    "time"
    "sync"
    "sync/atomic"
)
 
func main() {
    test1()
    test2()
}
 
func test1() {
    var wg sync.WaitGroup
    count := 0
    t := time.Now()
    for i := 0 ; i < 50000 ; i++ {
        wg.Add(1)
        go func(wg *sync.WaitGroup,i int) {
            count++ //count不是并發安全的
            wg.Done()
        }(&wg,i)
    }
 
    wg.Wait()
    fmt.Println(time.Now().Sub(t))
    fmt.Println("count====>",count) //count的值<50000
    fmt.Println("exit")
}
 
func test2() {
    var wg sync.WaitGroup
    count := int64(0)
    t := time.Now()
    for i := 0 ; i < 50000 ; i++ {
        wg.Add(1)
        go func(wg *sync.WaitGroup,i int) {
            atomic.AddInt64(&count,1) //原子操作
            wg.Done()
        }(&wg,i)
    }
 
    wg.Wait()
    fmt.Println(time.Now().Sub(t))
    fmt.Println("count====>",count) //count的值為50000
    fmt.Println("exit")
}

執行結果:

?
1
2
3
4
5
6
18.0485ms
count====> 46621
exit
16.0418ms
count====> 50000
exit

補充:golang 基于共享變量的并發

并發定義:當我們沒有辦法自信地確認一個事件是在另一個事件的前面或者后面發生的話,就說明x和y這兩個事件是并發的。

并發安全:如果其所有可訪問的方法和操作都是并發安全的話,那么類型便是并發安全的。

競爭條件:程序在多個goroutine交叉執行操作時,沒有給出正確的結果。

只要有

兩個goroutine并發訪問

同一變量,且至

少其中的一個是寫操作的時候就會發生數據競爭。

數據競爭會在兩個以上的goroutine并發訪問相同的變量且至少其中一個為寫操作時發生。

第一種:不要去寫變量,變量直接提前初始化。

第二種:多個只允許一個goroutine訪問變量,用select來監聽操作(go的金句:不要通過共享變量來通信,通過通信(channel)來共享變量)。

第三種:允許過個goroutine訪問變量,但是同一時間只允許一個goroutine訪問。

現在我們來講第三種情況具體操作

 

golang 我們可以通過channel作為計量器,可以保證可以有多少個goroutine可以同時訪問。make(chan struct{},1),通過寫入讀取用阻塞的方式鎖定住指定的代碼塊的訪問。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var (
sema = make(chan struct{}, 1) // a binary semaphore guarding balance
balance int
)
func Deposit(amount int) {
sema <- struct{}{} // acquire token
balance = balance + amount
<-sema // release token
}
func Balance() int {
sema <- struct{}{} // acquire token
b := balance
<-sema // release token
return b
}

可以保證同一時刻只有一個goroutine來訪問。

然而我們可以用sync包中的Mutex來實現上面的功能,那就是:

互斥鎖 sync.Mutex

互斥鎖:保證共享變量不會被并發訪問。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import "sync"
var (
mu sync.Mutex // guards balance
balance int
)
func Deposit(amount int) {
mu.Lock()
balance = balance + amount
mu.Unlock()
}
func Balance() int {
mu.Lock()
b := balance
mu.Unlock()
return b
}

在Lock和Unlock之間的代碼段中的內容goroutine可以隨便讀取或者修改,這個代碼段叫做臨界區。

注意:一定要釋放鎖(Unlock),不管任何情況,可以利用defer Mutex.Unlock(),一定要注意go里沒有重入鎖,如果遇到更小原子的操作,考慮分解成不帶鎖功能的小塊函數

接下來我們將另一種鎖:讀寫鎖sync.RWMutex

很多情況我們需要保證讀的性能,而互斥鎖會短暫的阻止其他的goroutine的運行,沒法達到很好的多并發效果(多讀單寫),這時讀寫鎖就可以很好的解決這個問題。

RLock()和RUnlock()獲取和釋放一個讀取或者共享鎖。RLock只能在臨界區共享變量沒有任何寫入操作時可用。一般來說,我們不應該假設邏輯上的只讀函數/方法也不會去更新某一些變量。如果沒法確定,那么久使用互斥鎖(Mutex)

最后我們來講下內存同步的問題

?
1
2
3
4
5
6
7
8
9
var x, y int
go func() {
x = 1 // A1
fmt.Print("y:", y, " ") // A2
}()
go func() {
y = 1 // B1
fmt.Print("x:", x, " ") // B2
}()

上面的例子:A1、A2、B1、B2 執行循序卻是毫無規律

在現代計算機中可能會有一堆處理器,每一個都會有其本地緩存(local cache)。為了效率,對內存的寫入一般會在每一個處理器中緩沖,并在必要時一起flush到主存。這種情況下這些數據可能會以與當初goroutine寫入順序不同的順序被提交到主存。導致程序運行串行了,又同時串行的代碼訪問了共享變量,盡管goroutine A中一定需要觀察到x=1執行成功之后才會去讀取y,但它沒法確保自己觀察得到goroutine B中對y的寫入,所以A還可能會打印出y的一個舊版的值。

有兩種方法解決:

 

1.變量限定在goroutine中使用,不訪問共享變量

2.用互斥條件訪問

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。如有錯誤或未考慮完全的地方,望不吝賜教。

原文鏈接:https://skaygo.blog.csdn.net/article/details/81748121

延伸 · 閱讀

精彩推薦
  • Golanggolang如何使用struct的tag屬性的詳細介紹

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

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

    Go語言中文網11352020-05-21
  • Golanggolang json.Marshal 特殊html字符被轉義的解決方法

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

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

    李浩的life12792020-05-27
  • Golanggolang 通過ssh代理連接mysql的操作

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

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

    a165861639710342021-03-08
  • Golanggo語言制作端口掃描器

    go語言制作端口掃描器

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

    腳本之家3642020-04-25
  • Golanggolang的httpserver優雅重啟方法詳解

    golang的httpserver優雅重啟方法詳解

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

    helight2992020-05-14
  • GolangGolang中Bit數組的實現方式

    Golang中Bit數組的實現方式

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

    天易獨尊11682021-06-09
  • Golanggo日志系統logrus顯示文件和行號的操作

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

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

    SmallQinYan12302021-02-02
  • GolangGolang通脈之數據類型詳情

    Golang通脈之數據類型詳情

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

    4272021-11-24
主站蜘蛛池模板: 99久久精品99999久久 | 青青草在线观看 | 538精品视频在线观看 | 国产123区在线视频观看 | 国产盗摄女厕美女嘘嘘 | 3d美女触手怪爆羞羞漫画 | 国产第2页 | 日韩国产成人 | 国产欧美视频一区二区三区 | 日韩精品免费看 | 日本爽p大片免费观看 | 夫妻性生活在线 | 日本视频免费在线播放 | 好男人天堂网 | 高清视频在线播放 | 寡妇快点好大好爽视频 | 日本生活中的玛丽 | 欧美日韩国产精品综合 | daring国家队在线观看樱花动漫 | 22sihu国产精品视频影视资讯 | 国产白白视频在线观看2 | 四虎884aa永久播放地址http | 91色porny| 亚洲国产精品免费在线观看 | 石原莉奈adn093店长未婚妻 | heyzo在线观看 | 日韩一二三 | 91亚洲精品久久91综合 | 草逼视频免费看 | porno18老师hd| 亚洲精品αv一区二区三区 亚洲精品91大神在线观看 | 国产精品香蕉在线观看不卡 | 人人澡人 | 成年性生交大片免费看 | 末代皇帝无删减版在线观看 | 午夜免费无码福利视频麻豆 | 草草在线影院 | 国产精品林美惠子在线观看 | 四虎精品免费视频 | 久久一本综合 | 亚洲精品123区在线观看 |