go語言-Goroutine和通道

goroutine

  • 順序執行 :一個任務完成之後再執行下一個
  • 並行: 不必等到一個操作執行完畢後再執行下一個

併發和並行

併發就是同時處理很多事情,而並行就是同時做很多事情。

  • 並行:指同一時刻同時進行,進程並行需要多個處理器支持。
  • 併發:指一段時間內的多個進程輪流切換使CPU。

模擬阻塞執行

// Filename: go.org[*Org Src go.org[ go ]*]
// coding:utf-8
// 模擬阻塞的代碼
package main

import (
        "fmt"
        "time"
)

func main () {
        slowFunc()
        fmt.Println("I'm not shown until slowFunc() completes.")
}

// 
func slowFunc () {
        fmt.Println("Sleepper() start")
        time.Sleep(time.Second)
        fmt.Println("Sleepper() finished")
}

goroutine 使用: 只需要讓Goroutine執行的函數或方法前加上關鍵字go即可

使用 go關鍵字

// Filename: go.org[*Org Src go.org[ go ]*]
// coding:utf-8
// 使用goroutine
package main

import (
        "fmt"
        "time"
)

func main () {
        go slowFunc()
        fmt.Println("I'm not shown straightaway!")
}

// 
func slowFunc () {
        fmt.Println("Sleeper() started")
        time.Sleep(time.Second)
        fmt.Println("Sleeper() finished")
}


程序輸出一行就直接退出了,因爲goroutine 立即返回,程序直接執行後面的代碼,然後退出,如果沒有其他因素阻止,程序將在 goroutine 返回前退出。

阻止程序直接退出

// Filename: go.org[*Org Src go.org[ go ]*]
// coding:utf-8
// 使用goroutine
import (
        "fmt"
        "time"
)

func main () {
        go slowFunc()
        fmt.Println("I'm not shown straightaway!")
        time.Sleep(time.Second * 2)
}

// 
func slowFunc () {
        fmt.Println("Sleeper() started")
        time.Sleep(time.Second)
        fmt.Println("Sleeper() finished")
}

Goroutine

  • go 在幕後使用線程來管理併發,但Goroutine 讓程序員無需直接管理線程。

  • 創建一個Goroutine 只需要佔用幾KB內存,因此即便創建數千個 Goroutine 也不會耗盡內存。

  • 另外創建和銷燬Goroutine 的效率也非常的高。

  • 併發地執行代碼意味着程序可能更快的執行完畢,並在數據就緒後就返回,而無需等待程序的其他部分結束。

  • 阻塞式的代碼讓程序暫停執行。

  • 使用並法編程的情景包括從磁盤文件讀取數據,從網絡讀寫數據以及從數據庫讀寫數據。

通道 chan

  • 通道和 Goroutine 一起提供了一個受控的環境,通道讓 Goroutine 能夠互相通信,用於開發併發軟件。

如果說 Goroutine 是一種支持併發編程的方式,那麼通道就是一種與 Goroutine 通信的方式。 通道讓數據能夠進入和離開 Goroutine, 可方便 Goroutine 之間的通信。

  • 不要通過共享內存來通信,而通過通信來共享內存
  • 通道只能有一種數據類型,可以創建任意類型的通道,因此可以使用結構體來存儲複雜的數據結構

使用通道進行通信

// Filename: go.org[*Org Src go.org[ go ]*]
// coding:utf-8
// 使用通道進行通信

import (
        "fmt"
        "time"
)

func main () {
        c := make(chan string)
        go slowFunc(c)

        msg := <-c
        fmt.Println(msg)
}

// 
func slowFunc (c chan string) {
        time.Sleep(time.Second * 2)
        c <- "slowFunc() finished"
}

使用緩衝通道

  • 緩衝通道只能存儲指定數量的消息

如果向它發送更多的消息,將導致錯誤

  • close 用來關閉通道,禁止再向通道發送消息
// Filename: go.org[*Org Src go.org[ go ]*]
// coding:utf-8
// 
import (
        "fmt"
        "time"
)

func main () {
        messages := make(chan string, 2)
        messages <- "hello"
        messages <- "world"

        close(messages)
        fmt.Println("Pushed two messages onto Channel with no receivers")
        time.Sleep(time.Second)
        reveiver(messages)
}

// 
func reveiver (c chan string) {
        for msg := range c {
                fmt.Println(msg)
        }
}

流程控制

  • 給通道指定消息接收者是一個阻塞操作,因爲它將阻止函數返回,直到收到一條消息爲止。
  • 從通道接收並打印消息的程序需要阻塞,以免終止。

通道接收一條消息就返回

// Filename: go.org[*Org Src go.org[ go ]*]
// coding:utf-8
// 通道接收一條消息就返回

import (
        "fmt"
        "time"
)

func main () {
        messages := make(chan string)
        go pinger(messages)
        msg := <- messages
        fmt.Println(msg)
}

// 
func pinger (c chan string) {
        t := time.NewTicker(1 * time.Second)
        for {
                c <- "ping"
                <- t.C
        }
}

創建不斷監聽通道中消息的監聽器

// Filename: go.org[*Org Src go.org[ go ]*]
// coding:utf-8
// 不間斷的指定接收操作
import (
        "fmt"
        "time"
)

func main () {
        messages :=  make(chan string)
        go pinger(messages)
        for i := 0 ; i < 5 ; i ++ {
                msg := <- messages
                fmt.Println(msg)
        }
}

// 
func pinger (c chan string) {
        t := time.NewTicker(1 * time.Second)
        for {
                c <- "ping"
                <- t.C
        }
}

將通道作爲函數參數

  • 可以進一步指定在函數中如何使用傳入的參數,可在傳遞通道時指定爲只讀,只寫或讀寫的。

指定通道訪問權限

func channelReader(messages <-chan string){
        mes := messages
        fmt.Println(msg)
}

// 通道在函數內只寫的
func channelWriter (messages chan<- string) {
        messages <- "messages"
}

// 沒有指定箭頭,表示通道可讀可寫
func channelReaderWriter (messages chan string) {
        msg := <- messages
        fmt.Println(msg)
        messages <- "helloworld"
}

select 語句

結合使用 select 語句和通道

// Filename: go.org[*Org Src go.org[ go ]*]
// coding:utf-8
// select 語句使用

import (
        "fmt"
        "time"
)

func main () {
        channel1 := make(chan string)
        channel2 := make(chan string)

        go ping1(channel1)
        go ping2(channel2)

        select {
        case msg1 := <- channel1:
                fmt.Println("reveived", msg1)
        case msg2 := <- channel2:
                fmt.Println("reveived", msg2)
        }
}

// 
func ping1 (c chan string) {
        time.Sleep(time.Second)
        c <- "ping on channel1"
}

// 
func ping2 (c chan string) {
        time.Sleep(time.Second * 2)
        c <- "ping on channel2"
}
  • 哪條消息最先到達,將執行哪條case 語句
  • 即根據最先收到的消息採取相應的措施

給 select 語句指明超時時間

// Filename: go.org[*Org Src go.org[ go ]*]
// coding:utf-8
// 給select 語句指定超時時間

import (
        "fmt"
        "time"
)

func main () {
        channel1 := make(chan string)
        channel2 := make(chan string)

        go ping1(channel1)
        go ping2(channel2)

        select {
        case msg1 := <- channel1:
                fmt.Println("reveived", msg1)
        case msg2 := <- channel2:
                fmt.Println("reveived", msg2)
        case <- time.After(500 * time.Millisecond):
                fmt.Println("no messages receiverd. giving up")
        }
}

// 
func ping1 (c chan string) {
        time.Sleep(time.Second)
        c <- "ping on channel1"
}

// 
func ping2 (c chan string) {
        time.Sleep(time.Second * 2)
        c <- "ping on channel2"
}

退出通道

在已知需要停止的時間的情況下,使用超時時間是不錯的選擇,在不確定 select 語句該在何時返回, 因此不能使用定時器。 在這種情況下可以使用退出通道。

  • 通過在 select 語句中添加一個退出通道,可向退出通道發送消息來結束該語句,從而停止阻塞。
  • 關閉緩衝意味着不能再向它發送消息。緩衝的消息會被保留,可供接收者讀取。
// Filename: go.org[*Org Src go.org[ go ]*]
// coding:utf-8
// 使用退出通道

import (
        "time"
        "fmt"
)

func main () {
        messages :=  make(chan string)
        stop := make(chan bool)

        go sender(messages)
        go func() {
                time.Sleep(time.Second * 2)
                fmt.Println("time is up!")
                stop <- true
        }()

        for {
                select {
                case <- stop :
                        return
                case msg := <- messages:
                        fmt.Println(msg)
                }
        }
}

// 
func sender (c chan string) {
        t := time.NewTicker(time.Second)
        for {
                c <- "I'm sending a messages"
                <- t.C
        }
}

小練習

接收10 條消息後退出程序

// Filename: goroutine_chan.org[*Org Src goroutine_chan.org[ go ]*]
// coding:utf-8
// 通道接收後退出

import (
        "fmt"
        "time"
)

func main () {
        messages := make(chan string)

        go sender(messages)

        for i := 0; i < 10; i++ {
                msg :=  <- messages
                fmt.Println(msg)
        }
}

// send messages
func sender (c chan string) {
        t := time.NewTicker(time.Second)
        for {
                c <- "This is a messages."
                <- t.C
        }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章