1. 程式人生 > 其它 >golang中的sync

golang中的sync

1.Go語言中可以使用sync.WaitGroup來實現併發任務的同步

package main

import (
	"fmt"
	"sync"
)

func hello(wg *sync.WaitGroup) {
	defer wg.Done()
	fmt.Println("hello")
}

func main() {
	var wg sync.WaitGroup
	wg.Add(1)
	//go hello(wg)  // 注意:wg是一個結構體,傳遞的時候需要傳遞指標,此處傳遞的結構體,導致wg.Wait()一直等待,死鎖
	go hello(&wg)

	wg.Wait()
	fmt.Println("main over")
}

  需要注意sync.WaitGroup是一個結構體,傳遞的時候要傳遞指標。

2.sync.Once

在程式設計的很多場景下我們需要確保某些操作在高併發的場景下只執行一次,例如只加載一次配置檔案、只關閉一次通道等。

Go語言中的sync包中提供了一個針對只執行一次場景的解決方案–sync.Once。

sync.Once只有一個Do方法

package main

import (
"fmt"
"log"
"net"
"os"
"sync"
)

var printPid sync.Once

func main() {
listener, err := net.Listen("tcp", "127.0.0.1:18888")
if err != nil {
log.Fatalln(err)
return
}
defer listener.Close()

for {
// 注意:無論for迴圈遍歷多少次,此處的Do方法只會執行一次
printPid.Do(func() {
fmt.Println("當前程序的ID是:", os.Getpid())
})

conn, err := listener.Accept()
if err != nil {
log.Fatalln("監聽埠錯誤", err)
}

log.Println(conn.RemoteAddr(), "連結成功")
//go handleConn(conn) // 開啟一個goroutine來處理新到來客戶端連結
}
}

 sync.Once結構體內部包含了一個互斥鎖和布林值,互斥鎖保證布林值和資料的安全,而布林值用來記錄初始化是否完成,

  這樣設計就能保證初始化操作的時候是併發安全的,並且初始化操作也不會被執行多次

 

3.sync.Map

  go語言中內建的map不是併發安全的

package main

import (
	"fmt"
	"strconv"
	"sync"
)

var m = make(map[string]int)

func get(key string) int {
	return m[key]
}
func set(key string, value int) {
	m[key] = value
}

func main() {
	// 內建的map不是併發安全的
	wg := sync.WaitGroup{}
	for i := 0; i < 5; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			key := strconv.Itoa(i)
			set(key, i)
			fmt.Printf("key:%v, value:%v\n", key, get(key))
		}(i)
	}
	wg.Wait()
}

上面的程式碼開啟少量幾個goroutine的時候可能沒什麼問題,當併發多了之後執行上面的程式碼就會報fatal error: concurrent map writes錯誤。

像這種場景下就需要為map加鎖來保證併發的安全性了,Go語言的sync包中提供了一個開箱即用的併發安全版map–sync.Map。開箱即用表示不用像內建的map一樣使用make函式初始化就能直接使用。同時sync.Map內建了諸如Store、Load、LoadOrStore、Delete、Range等操作方法。

package main

import (
	"fmt"
	"strconv"
	"sync"
)

func main() {
	// go語言的sync包提供了開箱即用的併發安全的map-sync.Map,
	// 開箱即用表示不用向內建的map一樣使用make函式來初始化就能直接使用
	var (
		m = sync.Map{}
		wg sync.WaitGroup
	)

	for i := 0; i < 20; i++ {
		wg.Add(1)
		go func(i int) {
			key := strconv.Itoa(i)
			m.Store(key, i)  // map賦值
			v, _ := m.Load(key)
			fmt.Printf("key:%v, value:%v\n", key, v)

			defer wg.Done()
		}(i)
	}

	wg.Wait()

}