golang 線程與通道

首先咱們來看線程,在golang裏面也叫goroutinegolang

在讀這篇文章以前,咱們須要瞭解一下併發與並行。golang的線程是一種併發機制,而不是並行。它們之間的區別你們能夠上網搜一下,網上有不少的介紹。
多線程

下面咱們先來看一個例子吧併發

import(函數

         "fmt"spa

)
線程

funcmain(){it

    go fmt.Println("1")
    fmt.Println("2")    
}

在golang裏面,使用go這個關鍵字,後面再跟上一個函數就能夠建立一個線程。後面的這個函數能夠是已經寫好的函數,也能夠是一個匿名函數import

funcmain(){
匿名函數

        vari=3循環

    go func(a int) {
        fmt.Println(a)
        fmt.Println("1")
    }(i)
    fmt.Println("2")
  
}

上面的代碼就建立了一個匿名函數,而且還傳入了一個參數i,下面括號裏的i是實參,a是形參。

那麼上面的代碼能按照咱們預想的打印一、二、3嗎?告訴大家吧,不能,程序只能打印出2。下面我把正確的代碼貼出來吧

import(

    "fmt"
    "time"    
)

funcmain(){

    var i = 3
    go func(a int) {
        fmt.Println(a)
        fmt.Println("1")
    }(i)
    fmt.Println("2") time.Sleep(1 * time.Second) 
}

我只是在最後加了一行讓主線程休眠一秒的代碼,程序就會依次打印出二、三、1。

那爲何會這樣呢?由於程序會優先執行主線程,主線程執行完成後,程序會當即退出,沒有多餘的時間去執行子線程。若是在程序的最後讓主線程休眠1秒鐘,那程序就會有足夠的時間去執行子線程。


線程先講到這裏,下面咱們來看看通道吧。

通道又叫channel,顧名思義,channel的做用就是在多線程之間傳遞數據的。

建立無緩衝channel

chreadandwrite :=make(chan int)

chonlyread := make(<-chan int) //建立只讀channel
chonlywrite := make(chan<- int) //建立只寫channel

下面咱們來看一個例子:

        ch :=make(chan int)     

    ch <- 1 
     go func() {
        <-ch
        fmt.Println("1")
      }()
      fmt.Println("2"     

這段代碼執行時會出現一個錯誤: :=make(chan int,1)

 

    ch <- 1
    go func() {
        v := <-ch
        fmt.Println(v)
    }()
    time.Sleep(1 * time.Second)
    fmt.Println("2")

這樣的話程序就會依次打印出一、2

二、把ch<-1這一行代碼放到子線程代碼的後面,代碼以下:

       ch :=make(chan int)

    go func() {
        v := <-ch
        fmt.Println(v)
    }()
    ch <- 1
    fmt.Println("2")

這裏就不用讓主線程休眠了,由於channel在主線程中被賦值後,主線程就會阻塞,直到channel的值在子線程中被取出。


最後咱們看一個生產者和消費者的例子:

import (

    "fmt"
    "time"
)
func produce(p chan<- int) {
    for i := 0; i < 10; i++ {
        p <- i
        fmt.Println("send:", i)
    }
}
func consumer(c <-chan int) {
    for i := 0; i < 10; i++ {
        v := <-c
        fmt.Println("receive:", v)
    }
}
func main() {
    ch := make(chan int)
    go produce(ch)
    go consumer(ch)
    time.Sleep(1 * time.Second)
}

在這段代碼中,由於channel是沒有緩衝的,因此當生產者給channel賦值後,生產者這個線程會阻塞,直到消費者線程將channel中的數據取出。消費者第一次將數據取出後,進行下一次循環時,消費者的線程也會阻塞,由於生產者尚未將數據存入,這時程序會去執行生產者的線程。程序就這樣在消費者和生產者兩個線程間不斷切換,直到循環結束。


下面咱們再看一個帶緩衝的例子:

import (

    "fmt"
    "time"
)
func produce(p chan<- int) {
    for i := 0; i < 10; i++ {
        p <- i
        fmt.Println("send:", i)
    }
}
func consumer(c <-chan int) {
    for i := 0; i < 10; i++ {
        v := <-c
        fmt.Println("receive:", v)
    }
}
func main() {
    ch := make(chan int, 10)
    go produce(ch)
    go consumer(ch)
    time.Sleep(1 * time.Second)
}

在這個程序中,緩衝區能夠存儲10個int類型的整數,在執行生產者線程的時候,線程就不會阻塞,一次性將10個整數存入channel,在讀取的時候,也是一次性讀取。

相關文章
相關標籤/搜索