2019-05-23 Go語言學(xué)習四 并發(fā)

一、Go程

Go 程(goroutine)是由 Go 運行時管理的輕量級線程。

go f(x, y, z)

會啟動一個新的 Go 程并執(zhí)行

f(x, y, z)

f, x, y 和 z 的求值發(fā)生在當前的 Go 程中之众,而 f 的執(zhí)行發(fā)生在新的 Go 程中。

Go 程在相同的地址空間中運行,因此在訪問共享的內(nèi)存時必須進行同步所踊。sync 包提供了這種能力,不過在 Go 中并不經(jīng)常用到概荷,因為還有其它的辦法(見下一頁)秕岛。

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")
}

----------------------------------------------------------------------------
//運行結(jié)果:
hello
world
hello
world
hello
world
hello
world
hello

二、信道

(1)信道

信道是帶有類型的管道误证,你可以通過它用信道操作符 <- 來發(fā)送或者接收值继薛。

ch <- v    // 將 v 發(fā)送至信道 ch。
v := <-ch  // 從 ch 接收值并賦予 v愈捅。

(“箭頭”就是數(shù)據(jù)流的方向遏考。)
和映射與切片一樣,信道在使用前必須創(chuàng)建:

ch := make(chan int)

默認情況下蓝谨,發(fā)送和接收操作在另一端準備好之前都會阻塞灌具。這使得 Go 程可以在沒有顯式的鎖或競態(tài)變量的情況下進行同步青团。
以下示例對切片中的數(shù)進行求和,將任務(wù)分配給兩個 Go 程咖楣。一旦兩個 Go 程完成了它們的計算督笆,它就能算出最終的結(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)
}

//運行結(jié)果:-5 17 12

btw诱贿,Go程序并不會去保證這些goroutine會以怎樣的順序運行
所以哪個goroutine先執(zhí)行完娃肿,哪個goroutine后執(zhí)行完往往是不可預(yù)知的,除非我們使用了某種Go語言提供的方式進行人為干預(yù)珠十。

(2)帶緩沖的信道

信道可以是 帶緩沖的咸作。將緩沖長度作為第二個參數(shù)提供給 make 來初始化一個帶緩沖的信道:

ch := make(chan int, 100)

僅當信道的緩沖區(qū)填滿后,向其發(fā)送數(shù)據(jù)時才會阻塞宵睦。當緩沖區(qū)為空時记罚,接受方會阻塞。

package main

import "fmt"

func main() {
    ch := make(chan int, 2)
    ch <- 1
    ch <- 2
    fmt.Println(<-ch)
    fmt.Println(<-ch)
}

//運行結(jié)果:1 2

三壳嚎、range和close

發(fā)送者可通過 close 關(guān)閉一個信道來表示沒有需要發(fā)送的值了桐智。接收者可以通過為接收表達式分配第二個參數(shù)來測試信道是否被關(guān)閉:若沒有值可以接收且信道已被關(guān)閉,那么在執(zhí)行完

v, ok := <-ch

此時 ok 會被設(shè)置為 false烟馅。
循環(huán) for i := range c 會不斷從信道接收值说庭,直到它被關(guān)閉。

注意: 只有發(fā)送者才能關(guān)閉信道郑趁,而接收者不能刊驴。向一個已經(jīng)關(guān)閉的信道發(fā)送數(shù)據(jù)會引發(fā)程序恐慌(panic)。
還要注意: 信道與文件不同寡润,通常情況下無需關(guān)閉它們捆憎。只有在必須告訴接收者不再有需要發(fā)送的值時才有必要關(guān)閉,例如終止一個 range 循環(huán)梭纹。

package main

import (
    "fmt"
)

func fibonacci(n int, c chan int) {
    x, y := 0, 1
    for i := 0; i < n; i++ {
        c <- x
        x, y = y, x+y
    }
    close(c)
}

func main() {
    c := make(chan int, 10)
    go fibonacci(cap(c), c)
    for i := range c {
        fmt.Println(i)
    }
}

// 運行結(jié)果:
0
1
1
2
3
5
8
13
21
34

四躲惰、select語句

select 語句使一個 Go 程可以等待多個通信操作。
select 會阻塞到某個分支可以繼續(xù)執(zhí)行為止变抽,這時就會執(zhí)行該分支础拨。當多個分支都準備好時會隨機選擇一個執(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)
}

//運行結(jié)果:
0
1
1
2
3
5
8
13
21
34
quit

五绍载、默認選擇

當 select 中的其它分支都沒有準備好時诡宗,default 分支就會執(zhí)行。
為了在嘗試發(fā)送或者接收時不發(fā)生阻塞击儡,可使用 default 分支:

select {
case i := <-c:
    // 使用 i
default:
    // 從 c 中接收會阻塞時執(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)
        }
    }
}

// 運行結(jié)果: 
    .
    .
tick.
    .
    .
tick.
    .
    .
tick.
    .
    .
tick.
    .
    .
BOOM!

六塔沃、練習:等價二叉樹

不同二叉樹的葉節(jié)點上可以保存相同的值序列。例如曙痘,以下兩個二叉樹都保存了序列 1芳悲,1立肘,2边坤,3名扛,5,8茧痒,13肮韧。

圖片.png

在大多數(shù)語言中,檢查兩個二叉樹是否保存了相同序列的函數(shù)都相當復(fù)雜旺订。 我們將使用 Go 的并發(fā)和信道來編寫一個簡單的解法弄企。
本例使用了 tree 包,它定義了類型:

type Tree struct {
    Left  *Tree
    Value int
    Right *Tree
}
  1. 實現(xiàn) Walk 函數(shù)区拳。
  2. 測試 Walk 函數(shù)拘领。
    函數(shù) tree.New(k) 用于構(gòu)造一個隨機結(jié)構(gòu)的已排序二叉查找樹,它保存了值 k, 2k, 3k, ..., 10k樱调。
    創(chuàng)建一個新的信道 ch 并且對其進行步進:
go Walk(tree.New(1), ch)

然后從信道中讀取并打印 10 個值约素。應(yīng)當是數(shù)字 1, 2, 3, ..., 10。

  1. 用 Walk 實現(xiàn) Same 函數(shù)來檢測 t1 和 t2 是否存儲了相同的值笆凌。
  2. 測試 Same 函數(shù)圣猎。
    Same(tree.New(1), tree.New(1)) 應(yīng)當返回 true,而 Same(tree.New(1), tree.New(2)) 應(yīng)當返回 false乞而。
    Tree 的文檔可在這里找到
package main

import (
    "fmt"

    "golang.org/x/tour/tree"
)

// Walk 步進 tree t 將所有的值從 tree 發(fā)送到 channel ch送悔。
func Walk(t *tree.Tree, ch chan int) {
    if t.Left != nil {
        Walk(t.Left, ch)
    }
    ch <- t.Value
    if t.Right != nil {
        Walk(t.Right, ch)
    }
    return
    //close(ch)
}

// Same 檢測樹 t1 和 t2 是否含有相同的值。
func Same(t1, t2 *tree.Tree) bool {
    ch1, ch2 := make(chan int), make(chan int)
    go Walk(t1, ch1)
    go Walk(t2, ch2)
    if <-ch1 == <-ch2 {
        return true
    } else {
        return false
    }
}

func main() {
    ch := make(chan int)
    t1 := tree.New(1)
    t2 := tree.New(2)

    //從信道中打印10個值
    go Walk(t1, ch)
    for i := 0; i < 10; i++ {
        fmt.Println(<-ch)
    }

    //對tree1和tree2進行比較
    fmt.Println("tree 1 == tree 1:", Same(t1, t1))
    fmt.Println("tree 1 == tree 2:", Same(t1, t2))
}

-------------------------------------------------------------------------------------
// 運行結(jié)果:
tree 1 == tree 1: true
tree 1 == tree 2: false

七爪模、sync.Mutex

八欠啤、練習:Web爬蟲

習題講解:

插曲:

由于等價二叉樹程序代碼的實現(xiàn) 需要用到"golang.org/x/tour/tree"

這個包,而golang的官網(wǎng)需要翻墻才能進去屋灌。故后來采用的是在github找到相關(guān)來源的包跪妥,然后在linux環(huán)境下進行安裝下載。(方法一: 這是稍微老一點的方法声滥,新版本添加了mod的功能眉撵,可以不用設(shè)置GOPATH直接安裝,這將在方法二中記錄)
方法一:

使用命令為:

go get -x -v github.com/golang/sys

//輸入github賬號用戶名和密碼

  • btw落塑,命令 go get 可以根據(jù)要求和實際情況從互聯(lián)網(wǎng)上下載或更新指定的代碼包及其依賴包纽疟,并對它們進行編譯和安裝。
    在上面這個示例中憾赁,我們從代碼托管站點Github上下載了一個項目(或稱代碼包)污朽,并安裝到了環(huán)境變量GOPATH中包含的第一個工作區(qū)中。
    使用 go get 安裝命令龙考,系統(tǒng)會將下載好的安裝包自動放到環(huán)境變量GOPATH值中的第一個目錄路徑蟆肆。
    實際上矾睦,go get命令所做的動作也被叫做代碼包遠程導(dǎo)入,而傳遞給該命令的作為代碼包導(dǎo)入路徑的那個參數(shù)又被叫做代碼包遠程導(dǎo)入路徑炎功。
    more about go get

方法二:
明天學(xué)習內(nèi)容:熟悉go的基本命令和mod功能 以及第二種安裝方法 以及go語言小結(jié)
btw枚冗,CRT中文亂碼問題還沒解決(編碼已經(jīng)改至 UTF-8,在vim編輯中扔有亂碼現(xiàn)象)
可能的解決方法一

go的標準命令詳解
build install run install get mod

[Go語言之講解GOROOT蛇损、GOPATH赁温、GOBIN

golang包管理解決之道——go modules初探

再探go modules:使用與細節(jié)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市淤齐,隨后出現(xiàn)的幾起案子股囊,更是在濱河造成了極大的恐慌,老刑警劉巖更啄,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件稚疹,死亡現(xiàn)場離奇詭異,居然都是意外死亡祭务,警方通過查閱死者的電腦和手機内狗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來待牵,“玉大人其屏,你說我怎么就攤上這事∮Ц茫” “怎么了偎行?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長贰拿。 經(jīng)常有香客問我蛤袒,道長,這世上最難降的妖魔是什么膨更? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任妙真,我火速辦了婚禮,結(jié)果婚禮上荚守,老公的妹妹穿的比我還像新娘珍德。我一直安慰自己,他們只是感情好矗漾,可當我...
    茶點故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布锈候。 她就那樣靜靜地躺著,像睡著了一般敞贡。 火紅的嫁衣襯著肌膚如雪泵琳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天,我揣著相機與錄音获列,去河邊找鬼谷市。 笑死,一個胖子當著我的面吹牛击孩,可吹牛的內(nèi)容都是我干的迫悠。 我是一名探鬼主播,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼溯壶,長吁一口氣:“原來是場噩夢啊……” “哼及皂!你這毒婦竟也來了甫男?” 一聲冷哼從身側(cè)響起且改,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎板驳,沒想到半個月后又跛,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡若治,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年慨蓝,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片端幼。...
    茶點故事閱讀 39,745評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡礼烈,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出婆跑,到底是詐尸還是另有隱情此熬,我是刑警寧澤,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布滑进,位于F島的核電站犀忱,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏扶关。R本人自食惡果不足惜阴汇,卻給世界環(huán)境...
    茶點故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望节槐。 院中可真熱鬧搀庶,春花似錦、人聲如沸铜异。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽熙掺。三九已至未斑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間币绩,已是汗流浹背蜡秽。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工府阀, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人芽突。 一個月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓试浙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親寞蚌。 傳聞我的和親對象是個殘疾皇子田巴,可洞房花燭夜當晚...
    茶點故事閱讀 44,652評論 2 354

推薦閱讀更多精彩內(nèi)容