1. 程式人生 > >Go語言資料共享

Go語言資料共享

Go語言共享資料的安全性

Go語言保證協程間共享資料的安全性有兩種方式,一種是給共享變數加鎖,另一種是通過通道來共享資料。
下面通過簡單的模擬火車票的售票視窗,來看一下這兩種資料共享方式

第一種,通過通道共享

package main

import (
	"fmt"
	"sync"
	"time"
)

var (
	ticket      = 1 //火車票票號
	windNum     = 1000 //視窗的數量
	ticketTotal = 100000 //總票數
	wg          sync.WaitGroup
	lk          sync.Mutex
	ch          =
make(chan int, ticketTotal) ) func main() { //先把10萬張票寫入通道 for i := 1; i <= ticketTotal; i++ { ch <- i } //寫入操作完畢,關閉通道 close(ch) //等待1000個子協程執行完畢,再結束主函式。 wg.Add(windNum) now := time.Now() for i := 1; i <= windNum; i++ { go saleTic(i) } wg.Wait() end := time.Since(now) fmt.Println(end)
} func saleTic(n int) { defer wg.Done() for value := range ch { fmt.Print("視窗", n, "售票:", value, "\n") } fmt.Println("視窗", n, ":車票已售罄。") }

執行結果

視窗80售票:99268
視窗 80 :車票已售罄。
視窗 466 :車票已售罄。
視窗 715 :車票已售罄。
視窗 161 :車票已售罄。
視窗 989 :車票已售罄。
視窗550售票:92966
視窗 550 :車票已售罄。
視窗702售票:89661
視窗 702 :車票已售罄。
視窗 440
:車票已售罄。 356.392311ms

通過開啟1000個協程,模擬火車票售票視窗,每個協程代表一個售票視窗,總共售出10萬張火車票,最後356毫秒售完。

第二種方式

package main

import (
	"fmt"
	"sync"
	"time"
)

var (
	ticket      = 1
	windNum     = 1000
	ticketTotal = 100000
	wg3         sync.WaitGroup
	lk          sync.Mutex
)

func main() {
	wg3.Add(windNum)
	now := time.Now()
	for i := 1; i <= windNum; i++ {
		go saleTickets(i)
	}
	wg3.Wait()
	end := time.Since(now)
	fmt.Println(end)
}

func saleTickets(n int) {
	defer wg3.Done()
	for i := 1; i <= ticketTotal; i++ {
		lk.Lock()
		if ticket <= ticketTotal {
			fmt.Print("視窗", n, "售票:", ticket, "\n")
			ticket++
		} else {
			fmt.Println("視窗", n, ":車票已售罄。")
			lk.Unlock()
			break
		}
		lk.Unlock()
	}
}

執行結果

…………………省略…………………………………
視窗56售票:99994
視窗53售票:99995
視窗54售票:99996
視窗55售票:99997
視窗57售票:99998
視窗59售票:99999
視窗60售票:100000
視窗 46 :車票已售罄。
視窗 58 :車票已售罄。
視窗 61 :車票已售罄。
視窗 65 :車票已售罄。
視窗 62 :車票已售罄。
…………………省略…………………………………
視窗 52 :車票已售罄。
視窗 51 :車票已售罄。
視窗 47 :車票已售罄。
視窗 56 :車票已售罄。
視窗 53 :車票已售罄。
視窗 54 :車票已售罄。
視窗 55 :車票已售罄。
視窗 57 :車票已售罄。
視窗 59 :車票已售罄。
視窗 60 :車票已售罄。
415.865144ms

同樣模擬1000個售票視窗,總共售出10萬張票,最後456毫秒售完,這種最簡單的模擬高併發,不管是通過加鎖來保證共享資料安全,還是通過通道來共享資料,效率差不多,不過有一點區別是,加了鎖之後,車票是按順序售出的,車票是從1號到100000號依次被售賣的,不過具體是哪個視窗賣出的則不固定。
而通過通道來共享資料的話,售票的順序不固定,第一張被售出的車票很可能不是1號,最後一張被售出的車票也不是100000號。
通過通道共享資料,安全性高,不會出現一張票被重複出售;而另一種方式,如果saleTickets函式不加鎖,那麼會出現第1張票可能被重複出售了幾百次,這就是併發中的資料不安全性:當多個執行緒同時操作修改一個數據時,會發生同一個數字被多次使用的問題。
【舉個栗子】,就好像你的銀行賬戶上有10萬塊錢,你讓你爸拿存摺到銀行櫃檯取錢這10萬塊錢的同時,你又拿著你的銀行卡到自動取款機也取這10萬,同時你女朋友又用你的綁定了這張銀行卡的某寶賬號在某寶上買了個包包,你們3人掐好了時間,同時按下了取款按鈕或者點選了確認支付,那麼會發生什麼情況,你的這10萬塊錢被花了三次,也就是你卡上只有10萬塊錢,但是你卻用這張卡一下消費了三十萬,如果大家都這樣搞,那我們天朝的宇宙四大行早就歇菜了,那麼如何解決這個問題呢,就是加鎖,銀行系統會設定,當你在操作一個銀行賬號的時候,你往系統中輸入了這個賬號之後,系統就會鎖定這個賬號,其他任何人,在任何地點都不能再操作這個賬號了,只有當你按下了取款按鈕,自動取款機把錢吐出來,並且你按下了退卡按鈕以後,銀行系統才會解鎖這個賬戶,這時其他人才能操作這個賬號。
類似的,多執行緒中給共享變數加鎖的效果就是,在一個執行緒操作這個資料的時候,其他執行緒不能在操作這個資料,只有當這個執行緒結束之後,其他執行緒才能再次操作這個共享變數,這樣就保證了資料安全。