最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

淺析GO并發(fā)處理選擇sync還是channel

 更新時間:2023年08月29日 16:10:33   作者:阿兵云原生  
這篇文章主要想來和大家討論一下,GO?語言處理并發(fā)的時候我們是選擇sync還是channel,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下

如何選擇 sync 和 channel

以前寫 C 的時候,我們一般是都通過共享內(nèi)存來通信,對于并發(fā)去操作某一塊數(shù)據(jù)時,為了保證數(shù)據(jù)安全,控制線程間同步,我們們會去使用互斥鎖,加鎖解鎖來進行處理

然而 GO 語言中建議的時候通過通信來共享內(nèi)存,使用 channel 來完成臨界區(qū)的同步機制

可是 GO 語言中的 channel 畢竟是屬于比較高級的原語,自然在性能上就比不上 sync包里面的鎖機制,感興趣的同學(xué)可以自己寫一個簡單的基準(zhǔn)測試來確認(rèn)一下效果,評論去可以交流

另外,使用 sync 包來控制同步時,我們不會失去結(jié)構(gòu)對象的所有權(quán),還能讓多個協(xié)程之間同步訪問臨界區(qū)的資源,那么如果我們的需求能夠符合這種情況時,還是建議使用 sync 包來控制同步更加的合理和高效

為什么會選擇使用 sync 包來控制同步結(jié)論:

  • 不期望失去結(jié)構(gòu)的控制權(quán)的同時,還期望多個協(xié)程能夠安全的同步訪問臨界區(qū)資源
  • 對性能要求會更高的情況

sync 的 Mutex 和 RWMutex

查看 sync 包的源碼(xxx\Go\src\sync),我們可以看到 sync 包下面有如下幾個結(jié)構(gòu):

  • Mutex
  • RWMutex
  • Once
  • Cond
  • Pool
  • atomic 包原子操作

上述經(jīng)常使用的就是 Mutex 了,尤其是最開始不善于使用 channel 的時候,覺得使用 Mutex 非常的順手,其次 RWMutex 相對來說就會用的少一些

不知大家有沒有關(guān)注過,使用 Mutex 和 使用 RWMutex 的性能表現(xiàn),獲取大部分人都是默認(rèn)使用互斥鎖,一起寫個 demo 來看看 他倆的性能對比

var (
        mu   sync.Mutex
        murw sync.RWMutex
        tt1  = 1
        tt2  = 2
        tt3  = 3
)
// 使用 Mutex 控制讀取數(shù)據(jù)
func BenchmarkReadMutex(b *testing.B) {
        b.RunParallel(func(pp *testing.PB) {
                for pp.Next() {
                        mu.Lock()
                        _ = tt1
                        mu.Unlock()
                }
        })
}
// 使用 RWMutex 控制讀取數(shù)據(jù)
func BenchmarkReadRWMutex(b *testing.B) {
        b.RunParallel(func(pp *testing.PB) {
                for pp.Next() {
                        murw.RLock()
                        _ = tt2
                        murw.RUnlock()
                }
        })
}
// 使用 RWMutex 控制讀寫入數(shù)據(jù)
func BenchmarkWriteRWMutex(b *testing.B) {
        b.RunParallel(func(pp *testing.PB) {
                for pp.Next() {
                        murw.Lock()
                        tt3++
                        murw.Unlock()
                }
        })
}

寫了三個簡單的基準(zhǔn)測試

  • 使用互斥鎖讀取數(shù)據(jù)
  • 使用讀寫鎖的讀鎖讀取數(shù)據(jù)
  • 使用讀寫鎖讀取和寫入數(shù)據(jù)
$ go test -bench . bbb_test.go --cpu 2
goos: windows
goarch: amd64
cpu: Intel(R) Core(TM)2 Duo CPU     T7700  @ 2.40GHz
BenchmarkReadMutex-2            39638757                30.45 ns/op
BenchmarkReadRWMutex-2          43082371                26.97 ns/op
BenchmarkWriteRWMutex-2         16383997                71.35 ns/op
$ go test -bench . bbb_test.go --cpu 4
goos: windows
goarch: amd64
cpu: Intel(R) Core(TM)2 Duo CPU     T7700  @ 2.40GHz
BenchmarkReadMutex-4            17066666                73.47 ns/op
BenchmarkReadRWMutex-4          43885633                30.33 ns/op
BenchmarkWriteRWMutex-4         10593098               110.3 ns/op
$ go test -bench . bbb_test.go --cpu 8
goos: windows
goarch: amd64
cpu: Intel(R) Core(TM)2 Duo CPU     T7700  @ 2.40GHz
BenchmarkReadMutex-8             8969340               129.0 ns/op
BenchmarkReadRWMutex-8          36451077                33.46 ns/op
BenchmarkWriteRWMutex-8          7728303               158.5 ns/op
$ go test -bench . bbb_test.go --cpu 16
goos: windows
goarch: amd64
cpu: Intel(R) Core(TM)2 Duo CPU     T7700  @ 2.40GHz
BenchmarkReadMutex-16            8533333               132.6 ns/op
BenchmarkReadRWMutex-16         39638757                29.98 ns/op
BenchmarkWriteRWMutex-16         6751646               173.9 ns/op
$ go test -bench . bbb_test.go --cpu 128
goos: windows
goarch: amd64
cpu: Intel(R) Core(TM)2 Duo CPU     T7700  @ 2.40GHz
BenchmarkReadMutex-128          10155368               116.0 ns/op
BenchmarkReadRWMutex-128        35108558                33.27 ns/op
BenchmarkWriteRWMutex-128        6334021               195.3 ns/op

可以看出來當(dāng)并發(fā)較小的時候,使用互斥鎖和使用讀寫鎖的讀鎖性能類似,當(dāng)并發(fā)逐漸變大時,讀寫鎖的讀鎖性能并未發(fā)生較大變化,互斥鎖和讀寫鎖的性能都會隨著并發(fā)的變大而下降

那么很明顯,讀寫鎖適用于讀多寫少的場景,在大并發(fā)讀書數(shù)據(jù)的時候,多個協(xié)程可以同時拿到讀鎖,減少鎖競爭和等待時間

而互斥鎖并發(fā)的時候,多個協(xié)程中,只有一個協(xié)程能拿到鎖,其他協(xié)程就會阻塞和等待,影響性能

舉個例子,我們正常使用互斥鎖,看看可能會出現(xiàn)什么樣的問題

使用 sync 需要注意的地方

平時使用 sync 包中的鎖的時候,需要注意的是不要去拷貝已經(jīng)已經(jīng)使用過的 Mutex 或者是 RWMutex

寫一個簡單的 demo:

var mu sync.Mutex
// sync 的互斥鎖,讀寫鎖,在被使用之后,就不要去復(fù)制這個對象,若要復(fù)制,需要在其未被使用的時候
func main() {
    go func(mm sync.Mutex) {
            for {
                    mm.Lock()
                    time.Sleep(time.Second * 1)
                    fmt.Println("g2")
                    mm.Unlock()
            }
    }(mu)
    mu.Lock()
    go func(mm sync.Mutex) {
            for {
                    mm.Lock()
                    time.Sleep(time.Second * 1)
                    fmt.Println("g3")
                    mm.Unlock()
            }
    }(mu)
    time.Sleep(time.Second * 1)
    fmt.Println("g1")
    mu.Unlock()
    time.Sleep(time.Second * 20)
}

感興趣的朋友的,可以運行一下,可以看到打印的結(jié)果中時沒有 g3 的,因此 g3 所在的協(xié)程已經(jīng)發(fā)生了死鎖,沒有機會去調(diào)用 unlock

出現(xiàn)這種情況的原因是這樣的,先來看看 Mutex 的內(nèi)部結(jié)構(gòu):

//...
// A Mutex must not be copied after first use.
//...
type Mutex struct {
        state int32
        sema  uint32
}

因為例如 Mutex 中的內(nèi)部結(jié)構(gòu)是有一個 state (表示互斥鎖的狀態(tài))和 sema(表示控制互斥鎖的信號量),其中初始化 Mutex 的時候,他們都是 0,但是當(dāng)我們用 Mutex 加鎖時,Mutex 的狀態(tài)就變成了 Locked 的狀態(tài),這個時候,其中一個協(xié)程去拷貝這個 Mutex,并在自己協(xié)程中加鎖,就會出現(xiàn)死鎖的情況,這一點是非常需要注意的

如果涉及到這種多個協(xié)程使用 Mutex 的情況, 可以使用閉包或者傳入包裹鎖的結(jié)構(gòu)地址或者指針,這樣就可以避免使用鎖的時候?qū)е虏豢深A(yù)期的結(jié)果,避免一臉蒙圈

sync.Once

sync 包中的其他成員,不知 xdm 使用的多么,相對使用頻率較高的應(yīng)該就是 sync.Once 了,其他成員 xdm 可以自行看看源碼,或者評論區(qū)留言哦,我們來看看 syn.Once 如何使用,都有哪些需要注意的?

還記得之前寫 C 或者 C++ 的時候,對于程序生命周期只有一個實例的時候,我們會選擇使用單例模式來進行處理,那么此處的 sync.Once 就是非常適合用在單例模式中

sync.Once 可以保證任意一個函數(shù)在程序運行期間只被執(zhí)行一次,這一點相對來說就比每個包中的 init 函數(shù)靈活一些了

這里需要注意,sync.Once 中執(zhí)行的函數(shù),如果出現(xiàn)了 panic ,也是會被認(rèn)為是執(zhí)行完了了一次,之后如果再有邏輯需要進入 sync.Once 是無法進入并執(zhí)行函數(shù)邏輯的

一般情況下, sync.Once 用于對象資源的初始化和清理動作,避免重復(fù)操作,可以來看一個 demo:

  • 主函數(shù)開辟 3 個協(xié)程,且使用 sync.WaitGroup 來管控并等待子協(xié)程退出
  • 主函數(shù)開辟所有協(xié)程之后等待 2 秒,開始創(chuàng)建并獲取實例
  • 協(xié)程中也在獲取實例
  • 只要有一個協(xié)程獲取到進入 Once,執(zhí)行邏輯之后,會出現(xiàn) panic
  • 出現(xiàn) panic 的協(xié)程捕獲了異常,此時全局的 instance 已經(jīng)被初始化,其他協(xié)程仍然無法進入 Once 內(nèi)的函數(shù)
type Instance struct {
        Name string
}
var instance *Instance
var on sync.Once
func GetInstance(num int) *Instance {
        defer func() {
                if err := recover(); err != nil {
                        fmt.Println("num %d ,get instance and catch error ... \n", num)
                }
        }()
        on.Do(func() {
                instance = &Instance{Name: "阿兵云原生"}
                fmt.Printf("%d enter once ... \n", num)
                panic("panic....")
        })
        return instance
}
func main() {
        var wg sync.WaitGroup
        for i := 0; i < 3; i++ {
                wg.Add(1)
                go func(i int) {
                        ins := GetInstance(i)
                        fmt.Printf("%d: ins:%+v  , p=%p\n", i, ins, ins)
                        wg.Done()
                }(i)
        }
        time.Sleep(time.Second * 2)
        ins := GetInstance(9)
        fmt.Printf("9: ins:%+v  , p=%p\n", ins, ins)
        wg.Wait()
}

通過打印結(jié)果可以看出,0 對應(yīng)的協(xié)程進入了 Once,且發(fā)生了 panic,因此當(dāng)前協(xié)程獲取到的 GetInstance 函數(shù)的結(jié)果是 nil

其他的協(xié)程包括主協(xié)程調(diào)用 GetInstance 函數(shù)都能正常拿到 instance 的地址,可以看出地址是同一個,全局就只初始化了一次

$ go run main.go
0 enter once ...
num %d ,get instance and catch error ...
 0
0: ins:<nil>  , p=0x0
1: ins:&{Name:阿兵云原生}  , p=0xc000086000
2: ins:&{Name:阿兵云原生}  , p=0xc000086000
9: ins:&{Name:阿兵云原生}  , p=0xc000086000

到此這篇關(guān)于淺析GO并發(fā)處理選擇sync還是channel的文章就介紹到這了,更多相關(guān)go并發(fā)處理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • GO語言實現(xiàn)TCP服務(wù)器的示例代碼

    GO語言實現(xiàn)TCP服務(wù)器的示例代碼

    這篇文章主要為大家詳細(xì)介紹了如何通過GO語言實現(xiàn)TCP服務(wù)器,文中的示例代碼講解詳細(xì),對我們深入了解Go語言有一定的幫助,需要的可以參考一下
    2023-03-03
  • Go?CSV包實現(xiàn)結(jié)構(gòu)體和csv內(nèi)容互轉(zhuǎn)工具詳解

    Go?CSV包實現(xiàn)結(jié)構(gòu)體和csv內(nèi)容互轉(zhuǎn)工具詳解

    這篇文章主要介紹了Go?CSV包實現(xiàn)結(jié)構(gòu)體和csv內(nèi)容互轉(zhuǎn)工具詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-03-03
  • 深入了解Golang官方container/heap用法

    深入了解Golang官方container/heap用法

    在?Golang?的標(biāo)準(zhǔn)庫?container?中,包含了幾種常見的數(shù)據(jù)結(jié)構(gòu)的實現(xiàn),其實是非常好的學(xué)習(xí)材料。今天我們就來看看?container/heap?的源碼,了解一下官方的同學(xué)是怎么設(shè)計,我們作為開發(fā)者又該如何使用
    2022-10-10
  • Go讀取文件與寫入文件的三種方法操作指南

    Go讀取文件與寫入文件的三種方法操作指南

    在 Go 語言中也經(jīng)常會遇到操作文件的需求,下面這篇文章主要給大家介紹了關(guān)于Go讀取文件與寫入文件的三種方法操作,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-09-09
  • go常用指令之go?mod詳解

    go常用指令之go?mod詳解

    當(dāng)go命令運行時,它查找當(dāng)前目錄然后查找相繼的父目錄來找出 go.mod,下面這篇文章主要給大家介紹了關(guān)于go常用指令之go?mod的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-08-08
  • go grpc高級用法

    go grpc高級用法

    RPC是遠程過程調(diào)用,可以像調(diào)用本地服務(wù)一樣取調(diào)用遠程服務(wù),本文主要介紹了go grpc高級用法,具有一定的參考價值,感興趣的可以了解一下
    2024-01-01
  • 詳解如何使用Golang擴展Envoy

    詳解如何使用Golang擴展Envoy

    這篇文章主要為大家介紹了詳解如何使用Golang擴展Envoy實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-06-06
  • Go 語言下基于Redis分布式鎖的實現(xiàn)方式

    Go 語言下基于Redis分布式鎖的實現(xiàn)方式

    本篇文章將詳細(xì)介紹如何正確地實現(xiàn)Redis分布式鎖,下面通過一個項目基于 Redis 的分布式鎖能夠提供哪些分布鎖特性,本文給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2021-06-06
  • Golang中的閉包(Closures)詳解

    Golang中的閉包(Closures)詳解

    在?Golang?中,閉包是一個引用了作用域之外的變量的函數(shù),Golang?中的匿名函數(shù)也被稱為閉包,閉包可以被認(rèn)為是一種特殊類型的匿名函數(shù),所以本文就給大家詳細(xì)的介紹一下Golang的閉包到底是什么,感興趣的小伙伴跟著小編一起來看看吧
    2023-07-07
  • golang在GRPC中設(shè)置client的超時時間

    golang在GRPC中設(shè)置client的超時時間

    這篇文章主要介紹了golang在GRPC中設(shè)置client的超時時間,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04

最新評論

华坪县| 孟津县| 兴仁县| 司法| 班戈县| 资溪县| 蒙城县| 沽源县| 汶川县| 齐河县| 比如县| 孟连| 衢州市| 集贤县| 惠东县| 辽阳市| 许昌市| 潼南县| 清河县| 苍山县| 宁波市| 麻城市| 治县。| 忻州市| 邵阳县| 肃北| 阳春市| 西峡县| 汨罗市| 财经| 镇巴县| 陇川县| 南岸区| 扶绥县| 保康县| 公安县| 锦州市| 罗田县| 嘉峪关市| 关岭| 平安县|