學(xué)習(xí)筆記20170601">【PMP】學(xué)習(xí)筆記20170601
772
2022-05-30
Go is an open source programming language? that makes it wasy to build simple, reliable, and efficient software.
- golang.org
跟隨golang官網(wǎng)教程,學(xué)習(xí)Go語(yǔ)言基礎(chǔ)知識(shí) - 并發(fā), 參考鏈接https://tour.go-zh.org/list。Let's go!
并發(fā)
goroutine
Go 程(goroutine)是由 Go 運(yùn)行時(shí)管理的輕量級(jí)線程。
go?f(x,?y,?z)
會(huì)啟動(dòng)一個(gè)新的 Go 程并執(zhí)行
f(x,?y,?z)
f,?x,?y?和?z?的求值發(fā)生在當(dāng)前的 Go 程中,而?f?的執(zhí)行發(fā)生在新的 Go 程中。
Go 程在相同的地址空間中運(yùn)行,因此在訪問(wèn)共享的內(nèi)存時(shí)必須進(jìn)行同步。sync?包提供了這種能力,不過(guò)在 Go 中并不經(jīng)常用到,因?yàn)檫€有其它的辦法(見(jiàn)下一頁(yè))。
package?main import?( "fmt" "time" ) func?say(s?string)?{ for?i?:=?0;?i?5;?i++?{ time.Sleep(100?*?time.Millisecond) fmt.Println(s) } } func?main()?{ go?say("world") say("hello") } //?output world hello hello world world hello hello world world hello
信道
信道是帶有類(lèi)型的管道,你可以通過(guò)它用信道操作符?<-?來(lái)發(fā)送或者接收值。先進(jìn)先出!
ch?<-?v????//?將?v?發(fā)送至信道?ch。 v?:=?<-ch??//?從?ch?接收值并賦予?v。
(“箭頭”就是數(shù)據(jù)流的方向。)
和映射與切片一樣,信道在使用前必須創(chuàng)建:
ch?:=?make(chan?int)
默認(rèn)情況下,發(fā)送和接收操作在另一端準(zhǔn)備好之前都會(huì)阻塞。這使得 Go 程可以在沒(méi)有顯式的鎖或競(jìng)態(tài)變量的情況下進(jìn)行同步。
以下示例對(duì)切片中的數(shù)進(jìn)行求和,將任務(wù)分配給兩個(gè) Go 程。一旦兩個(gè) Go 程完成了它們的計(jì)算,它就能算出最終的結(jié)果。
package?main import?"fmt" func?sum(s?[]int,?c?chan?int)?{ sum?:=?0 for?_,?v?:=?range?s?{ sum?+=?v } c?<-?sum?//?將和送入?c } func?main()?{ s?:=?[]int{7,?2,?8,?-9,?4,?0} c?:=?make(chan?int) go?sum(s[:len(s)/2],?c) go?sum(s[len(s)/2:],?c) x,?y?:=?<-c,?<-c?//?從?c?中接收 fmt.Println(x,?y,?x+y) } //?output -5?17?12
帶緩沖的信道
信道可以是?帶緩沖的。將緩沖長(zhǎng)度作為第二個(gè)參數(shù)提供給?make?來(lái)初始化一個(gè)帶緩沖的信道:
ch?:=?make(chan?int,?100)
僅當(dāng)信道的緩沖區(qū)填滿后,向其發(fā)送數(shù)據(jù)時(shí)才會(huì)阻塞。當(dāng)緩沖區(qū)為空時(shí),接受方會(huì)阻塞。
修改示例填滿緩沖區(qū),然后看看會(huì)發(fā)生什么。
package?main import?"fmt" func?main()?{ ch?:=?make(chan?int,?2) ch?<-?1 ch?<-?2 ch?<-?3 fmt.Println(<-ch) fmt.Println(<-ch) } //?output fatal?error:?all?goroutines?are?asleep?-?deadlock! goroutine?1?[chan?send]: main.main() /tmp/sandbox029074443/prog.go:9?+0xa0
range 和 close
發(fā)送者可通過(guò)?close?關(guān)閉一個(gè)信道來(lái)表示沒(méi)有需要發(fā)送的值了。接收者可以通過(guò)為接收表達(dá)式分配第二個(gè)參數(shù)來(lái)測(cè)試信道是否被關(guān)閉:若沒(méi)有值可以接收且信道已被關(guān)閉,那么在執(zhí)行完
v,?ok?:=?<-ch
之后?ok?會(huì)被設(shè)置為?false。
循環(huán)?for i := range c?會(huì)不斷從信道接收值,直到它被關(guān)閉。
*注意:* 只有發(fā)送者才能關(guān)閉信道,而接收者不能。向一個(gè)已經(jīng)關(guān)閉的信道發(fā)送數(shù)據(jù)會(huì)引發(fā)程序恐慌(panic)。
*還要注意:* 信道與文件不同,通常情況下無(wú)需關(guān)閉它們。只有在必須告訴接收者不再有需要發(fā)送的值時(shí)才有必要關(guān)閉,例如終止一個(gè)?range?循環(huán)。
package?main import?( "fmt" ) func?fibonacci(n?int,?c?chan?int)?{ x,?y?:=?0,?1 for?i?:=?0;?i?
select 語(yǔ)句
select?語(yǔ)句使一個(gè) Go 程可以等待多個(gè)通信操作。
select?會(huì)阻塞到某個(gè)分支可以繼續(xù)執(zhí)行為止,這時(shí)就會(huì)執(zhí)行該分支。當(dāng)多個(gè)分支都準(zhǔn)備好時(shí)會(huì)隨機(jī)選擇一個(gè)執(zhí)行。
package?main import?"fmt" func?fibonacci(c,?quit?chan?int)?{ x,?y?:=?0,?1 for?{ select?{ case?c?<-?x: x,?y?=?y,?x+y case?<-quit: fmt.Println("quit") return } } } func?main()?{ c?:=?make(chan?int) quit?:=?make(chan?int) go?func()?{ for?i?:=?0;?i?10;?i++?{ fmt.Println(<-c) } quit?<-?0 }() fibonacci(c,?quit) } //?output 0 1 1 2 3 5 8 13 21 34 quit
默認(rèn)選擇
當(dāng)?select?中的其它分支都沒(méi)有準(zhǔn)備好時(shí),default?分支就會(huì)執(zhí)行。
為了在嘗試發(fā)送或者接收時(shí)不發(fā)生阻塞,可使用?default?分支:
select?{ case?i?:=?<-c: ????//?使用?i default: ????//?從?c?中接收會(huì)阻塞時(shí)執(zhí)行 }
package?main import?( "fmt" "time" ) func?main()?{ tick?:=?time.Tick(100?*?time.Millisecond) boom?:=?time.After(500?*?time.Millisecond) for?{ select?{ case?<-tick: fmt.Println("tick.") case?<-boom: fmt.Println("BOOM!") return default: fmt.Println("????.") time.Sleep(50?*?time.Millisecond) } } } //?output ????. ????. tick. ????.? ????. tick. ????.? ????. tick. ????. ????. tick. ????. ????. BOOM!
sync.Mutex
我們已經(jīng)看到信道非常適合在各個(gè) Go 程間進(jìn)行通信。
但是如果我們并不需要通信呢?比如說(shuō),若我們只是想保證每次只有一個(gè) Go 程能夠訪問(wèn)一個(gè)共享的變量,從而避免沖突?
這里涉及的概念叫做 *互斥(mutual*exclusion)* ,我們通常使用 *互斥鎖(Mutex)* 這一數(shù)據(jù)結(jié)構(gòu)來(lái)提供這種機(jī)制。
Go 標(biāo)準(zhǔn)庫(kù)中提供了?sync.Mutex?互斥鎖類(lèi)型及其兩個(gè)方法:
Lock
Unlock
我們可以通過(guò)在代碼前調(diào)用?Lock?方法,在代碼后調(diào)用?Unlock?方法來(lái)保證一段代碼的互斥執(zhí)行。參見(jiàn)?Inc?方法。
我們也可以用?defer?語(yǔ)句來(lái)保證互斥鎖一定會(huì)被解鎖。參見(jiàn)?Value?方法。
package?main import?( "fmt" "sync" "time" ) //?SafeCounter?的并發(fā)使用是安全的。 type?SafeCounter?struct?{ v???map[string]int mux?sync.Mutex } //?Inc?增加給定?key?的計(jì)數(shù)器的值。 func?(c?*SafeCounter)?Inc(key?string)?{ c.mux.Lock() //?Lock?之后同一時(shí)刻只有一個(gè)?goroutine?能訪問(wèn)?c.v c.v[key]++ c.mux.Unlock() } //?Value?返回給定?key?的計(jì)數(shù)器的當(dāng)前值。 func?(c?*SafeCounter)?Value(key?string)?int?{ c.mux.Lock() //?Lock?之后同一時(shí)刻只有一個(gè)?goroutine?能訪問(wèn)?c.v defer?c.mux.Unlock() return?c.v[key] } func?main()?{ c?:=?SafeCounter{v:?make(map[string]int)} for?i?:=?0;?i?1000;?i++?{ go?c.Inc("somekey") } time.Sleep(time.Second) fmt.Println(c.Value("somekey")) } //?output 1000
Go語(yǔ)言
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。