Sync包簡述
1. 什么是Sync包?
Package sync provides basic synchronization primitives such as mutual exclusion locks. Other than the Once and WaitGroup types, most are intended for use by low-level library routines. Higher-level synchronization is better done via channels and communication.
Values containing the types defined in this package should not be copied.
這句話大意是說:
Sync包同步提供基本的同步原語,如互斥鎖。 除了Once和WaitGroup類型之外,大多數(shù)類型都是供低級庫例程使用的。 通過Channel和溝通可以更好地完成更高級別的同步。并且此包中的值在使用過后不要拷貝。
從描述中可以看到的是,golang 并不推薦這個包中的大多數(shù)并發(fā)控制方法,但還是提供了相關(guān)方法,主要原因是golang中提倡以共享內(nèi)存的方式來通信:
不要以共享內(nèi)存的方式來通信,作為替代,我們應(yīng)該以通信的手段來共享內(nèi)存
共享內(nèi)存的方式使得多線程中的通信變得簡單,但是在并發(fā)的安全性控制上將變得異常繁瑣。
正確性不是我們唯一想要的,我們想要的還有系統(tǒng)的可伸縮性,以及可理解性,我覺得這點非常重要,比如現(xiàn)在廣泛使用的Raft算法。
2. 包中的Type
包中主要有: Locker, Cond, Map, Mutex, Once, Pool,
RWMutex, WaitGroup
type Locker interface {
Lock()
Unlock()
}
type Cond struct {
// L is held while observing or changing the condition
L Locker
}
3. 什么是鎖,為什么需要鎖?
鎖是sync包中的核心,他主要有兩個方法,加鎖和解鎖。
在單線程運行的時候程序是順序執(zhí)行的,程序?qū)?shù)據(jù)的訪問也是:
讀取 => 一頓操作(加減乘除之類的) => 寫回原地址
但是一旦程序中進行了并發(fā)編程,也就是說,某一個函數(shù)可能同時被不同的線程執(zhí)行的時候,以時間為維度會發(fā)生以下情況:

可以看到的是,A地址的數(shù)字被執(zhí)行了兩次自增,若A=5,我們在執(zhí)行完成后預(yù)期的A值是7,但是在這種情況下我們得到的A卻是6,bug了~
還有很多類似的并發(fā)錯誤,所以才有鎖的引入。若是我們在線程2讀取A的值的時候?qū)進行加鎖,讓線程2等待,線程1執(zhí)行完成之后在執(zhí)行線程2,這樣就能夠保證數(shù)據(jù)的正確性。但是正確性不是我們唯一想要的。
4 寫更優(yōu)雅的代碼
在很多語言中我們經(jīng)常為了保證數(shù)據(jù)安全正確,會在并發(fā)的時候?qū)?shù)據(jù)加鎖
Lock()
doSomething()
Unlock()
Golang在此包中也提供了相關(guān)的鎖,但是標明了"most are intended for use by low-level library routines" 所以我這里只對 Once and WaitGroup types做簡述。
5.Once 對象
Once 是一個可以被多次調(diào)用但是只執(zhí)行一次,若每次調(diào)用Do時傳入?yún)?shù)f不同,但是只有第一個才會被執(zhí)行。
func (o *Once) Do(f func())
var once sync.Once
onceBody := func() {
fmt.Println("Only once")
}
done := make(chan bool)
for i := 0; i < 10; i++ {
go func() {
once.Do(onceBody)
done <- true
}()
}
for i := 0; i < 10; i++ {
<-done
}
如果你執(zhí)行這段代碼會發(fā)現(xiàn),雖然調(diào)用了10次,但是只執(zhí)行了1次。BTW:這個東西可以用來寫單例。
6. WaitGroup
func (wg *WaitGroup) Add(delta int)
func (wg *WaitGroup) Done()
func (wg *WaitGroup) Wait()
wait group 用來等待一組goroutines的結(jié)束,在主Goroutine里聲明,并且設(shè)置要等待的goroutine的個數(shù),每個goroutine執(zhí)行完成之后調(diào)用 Done,最后在主Goroutines 里Wait即可。下面是個官方的例子:
var wg sync.WaitGroup
var urls = []string{
"http://www.golang.org/",
"http://www.google.com/",
"http://www.somestupidname.com/",
}
for _, url := range urls {
// Increment the WaitGroup counter.
wg.Add(1)
// Launch a goroutine to fetch the URL.
go func(url string) {
// Decrement the counter when the goroutine completes.
defer wg.Done()
// Fetch the URL.
http.Get(url)
}(url)
}
// Wait for all HTTP fetches to complete.
wg.Wait()
7. 簡述
Golang中高級的并發(fā)可以通過channel來實現(xiàn),這是golang所倡導(dǎo)的,但是go也提供了鎖等先關(guān)操作。