【名片全能王/啓信寶/掃描全能王】【乾貨貼】Goroutine & Channel

併發模型

進程 vs 線程 vs Goroutine

  • 進程,是操做系統分配資源的基本單元。不一樣的進程之間內存空間資源獨佔,只能經過信號、管道、文件等方式進行通訊。PHP-FPM即採起多進程併發模型,每個請求過來,都會fork一個獨立的進程用於處理該請求。
  • 線程,是操做系統調度的基本單元。同一進程下的不一樣線程之間共享內存,可能出現資源競爭等問題。Java Servlet即採用多線程併發模型,每個請求過來,都會建立一個獨立的線程用於處理該請求。因爲多線程使用共同的內存空間,就須要考慮全局性資源(全局的變量、對象、文件等)的線程安全問題。
  • Goroutine,是一種協程,即用戶空間的線程,操做系統不直接調度。相比進程和線程的優點,下文會具體闡述。

Goroutine & Channel

Golang使用CSP模型實現併發,goroutine和channel即分別對應CSP模型中的Process和Channel。不一樣於多線程併發模型須要在競態情形(race condition)下,經過複雜的鎖機制確保資源正確使用。goroutine之間可使用channel進行通訊。Channel能夠當作一個 FIFO 隊列,對 FIFO 隊列的讀寫都是原子的操做,不須要加鎖。html

Goroutine建立

  • 在Golang中,Goroutine是語言級別的支持,只須要經過一個簡單的go關鍵字便可建立Goroutine,十分快捷簡單。
go func() { // 開啓Goroutine
        fmt.Println("Hello World!")
    }()
複製代碼

Channel建立 & 關閉

  • Channel對象必須使用make()函數進行建立。
channel := make(chan int) // 建立Channel
    channel := make(chan int) // 建立Channel 
	channel := make(chan int) // 建立Channel
	buffered_channel := make(chan int, 2) // 建立帶有緩衝的Channel
	close(channel) // 關閉Channel
複製代碼

Channel發送 & 接收

  • 當"<-"發送運算符在Channel對象右邊時,表明發送操做。
  • 當"<-"接收運算符在Channel對象左邊時,表明接收操做。能夠只接收,不賦值;也能夠接收完,再經過"="賦值運算符賦值給一個變量。
package main

import (
    "fmt"
    "time"
)

func main() {
    channel := make(chan int) // 建立Channel
    go func() { // 開啓Goroutine
        time.Sleep(1000000000)
        channel <- 1 // 向Channel發送數據
    }()
    result := <-channel // 接收Channel中的數據
    fmt.Println(result)
}
複製代碼

select語句

  • select語句由多個帶有Channel的發送或接收操做的case組成。
  • 一組select語句執行,只會處理第一個就緒的發送或接收case。
package main

import (
    "fmt"
    "time"
)

func main() {
    channel1 := make(chan int) // 建立Channel1
    channel2 := make(chan int) // 建立Channel2
    go func() { // 開啓Goroutine1
        time.Sleep(1000000000)
        channel1 <- 1 // 向Channel1發送數據
    }()
    go func() { // 開啓Goroutine2
        time.Sleep(100000000)
        channel2 <- 2 // 向Channel2發送數據
    }()
    select { // 同時等待接收Channel1和Channel2的數據,只要有一個就緒,即完成對應case的處理
    case result := <-channel1:
        fmt.Println(result)
    case result := <-channel2:
        fmt.Println(result)
    }
}
複製代碼

for … range語句

  • for … range語句能夠迭代接收Channel中的數據,直到Channel被關閉。
package main

import (
    "fmt"
    "time"
)

func main() {
    channel := make(chan int) // 建立Channel
    go func() {
        for i := 0; i < 10; i++ {
            time.Sleep(1000000000)
            channel <- i // 不斷向Channel發送數據
        }
        close(channel) // 關閉Channel
    }()
    for result := range channel { // 迭代Channel中的數據,並打印出來
        fmt.Println(result)
    }
}
複製代碼

優雅地實現處理超時

  • golang標準庫中的time.After()函數的返回值,便是一個channel。
  • 經過和select語句相結合,咱們優雅地實現處理超時。
package main

import (
    "fmt"
    "time"
)

func main() {
    channel := make(chan int) // 建立Channel
    go func() { // 開啓Goroutine
        time.Sleep(1000000000)
        channel <- 1 // 向Channel發送數據
    }()

    select {
    case result := <-channel: // 接收Channel中的數據
        fmt.Println(result)
    case <-time.After(100000000):
        fmt.Println("time out")
    }
}
複製代碼

ps:目前誠招前端/後端/移動端社招&校招崗位!

相關文章
相關標籤/搜索