Golang channel 的基本使用方法

package main

import (
    "fmt"
    "learner/Add"
    "time"
)

//a. 普通類型,普通變量保存的就是值,也叫值類型
//b. 獲取普通變量的內存地址,用&,好比: var a int, 獲取a的內存地址:&a
//c. 指針類型,指針變量存的就是一個內存地址,這個地址指向值
//d. 獲取指針類型所指向的值,使用:*,好比:var *p int, 使用*p獲取p指向的值
//e. 將一個內存地址給一個指針類型進行賦值(不能夠直接將變量賦值給指針,須要將內存地址賦值給指針): var a int=5, var p *int = &a

// 在工程上有兩種最多見的併發通訊模型: 共享數據和消息通訊, go語言選擇後者,經過通訊進行共享內存
// channel, goroutine 間的通訊方式, 進程內的的通訊.進程間的通訊建議使用socket或者http等通訊協議.
// channel 是類型相關的, 一個channel只能傳遞一種指定類型的值, 這個值須要在聲明channel時指定(能夠理解爲指定元素類型的管道)

// 超時控制的經典實現
func chan_time_out_handler(ch chan int)  (item bool){
    // 使用 select 爲channel實現超時機制, select的一個case必須是一個面向channel的操做
    // 定義一個time_out chan
    timeOut := make(chan bool, 1)
    go func(){
        time.Sleep(1e9) // 等待一秒鐘
        timeOut<- true
    }()
    // 利用time_out這個chan實現超時以後作何操做
    select {
        case a := <- ch: // 嘗試從ch這個chan中讀取數據
            fmt.Println(a)
            return true
        case <- timeOut: // 在等待時間範圍內一直沒有從ch中讀取到了數據可是從time_out 這個 chan 中讀取到了數據
            return false
    }
}

// 只往chan中寫入數據
func chan_in(ch_in chan<- int)  {
    for i:=0; i <= 10; i++{
        ch_in <- 1
    }
    // 若是是使用range遍歷chan, 那麼當chan關閉時, 讀取操做會當即結束,跳出循環(注意,這是channel中可能仍會存在數據),
    // channel關閉後,其實仍然能夠從中讀取已發送的數據(使用range沒法實現, 可使用常規的循環讀取channel的方式),讀取完數據後,將讀取到零值,能夠屢次讀取(仍然是零值)
    close(ch_in) // 當多個goroutine都使用了同一個channel時, 任何一個goroutine 中關閉了這個了這個channel, 其餘goroutine將沒法繼續對這個channel進行讀取
}

// 只從chan讀出數據
func chan_out(ch_out <-chan int)  {
    // 使用range, 當這個channel關閉時,就會跳出循環,可是channel裏面仍然可能存在數據
    // x, ok := <- ch_out, 若是ok返回的是false,那麼就表示這個chan已經關閉
    for value := range ch_out{
        fmt.Printf("+++++++++++++++++%d", value)
    }
}

func main() {

    // 切片和map 都是指針類型的數據, 指針類型的數據均可以使用make()進行分配內存
    chs := make([]chan int, 10) // 定義一個切片並分配內存, 元素是chan類型, 這個chan內能夠保存的元素爲int類型, 該切片初始內存能夠保存10個元素(不是channel的緩衝區, 是切片的初始大小)
    for i := 0; i < 10; i++ {
        chs[i] = make(chan int, 3) // 定義一個chan並分配內存, 緩衝區大小爲3, 而後保存到切片中, 若是不設置緩衝區,當寫入一個元素時,若是這個元素不被讀取掉,寫操做將會被阻塞
        go Add.TestAddTwo(chs[i])  // 開啓協程發送chan
    }

    for _, ch := range (chs) {
        fmt.Println("====================", len(ch)) //當程序運行到這裏時, 這個channel有可能並無寫入數據,因此長度有可能爲0 1 2
        a := <-ch  // 當這裏從當前channel讀取不到數據時就會阻塞
        // b := <-ch  // 繼續讀取, 讀取不到就堵塞
        fmt.Println(a)

        item := chan_time_out_handler(ch)
        fmt.Println(item)

        // 當channel寫完數據操做完成後若是沒有關閉,讀取完數據,chan爲空時,將會阻塞, 從而有可能形成死鎖, 因此chan使用完必須關閉

    }

    // 單向channel的實現,當須要將一個單向的channel從讀變爲寫,或者從寫變爲讀時,須要進行類型轉換
    // 第一個步,定義一個正常的channel
    ch_normal := make(chan int)
    // 第二步進行類型轉換,將ch_normal 轉換爲只容許寫的channel
    var ch_in chan<- int = ch_normal
    go chan_in(ch_in)
    // 第三步 生成一個只容許進行讀的channel
    var ch_out  <-chan int = ch_normal
    chan_out(ch_out)
}


// 當向一個channel寫入數據, 在這個channel被讀取前, 這個操做是阻塞的(在緩衝區寫滿以前, 即便沒有讀取操做,寫操做都不會阻塞)
// 當從一個channel讀取數據時,在對應的channel寫入數據前, 這個操做也是阻塞的,從而能夠利用channel實現了相似鎖的功能, 進而保證
// 了全部goroutine完成後主函數才返回
// 緩衝區滿以後將會阻塞,除非有goroutine對其進行操做, 不然協程就會停留在向該channel寫入元素的步驟上, 直到主進程退出, 向channel寫入數據的協程也就退出. 協程的阻塞不影響主進程的執行

// 定義一個channel var chanName chan ElementType
// 多層定義,例如定義一個 map, 鍵是string類型,元素是bool類型的channel:  var myMap map[string] chan bool
// 聲明之後,定義一個channel 並賦值給變量: map["firstChan"] := make(chan false) , 使用內建函數make()

// 若是是使用range遍歷chan, 那麼當chan關閉時, 讀取操做會當即結束,跳出循環(注意,這是channel中可能仍會存在數據)
// 當多個goroutine都使用了同一個channel時, 任何一個goroutine 中關閉了這個了這個channel,
// 其餘goroutine將沒法繼續對這個channel進行讀取, 能夠在主進程中進行守護, 等全部的goroutine執行完畢後再去關閉channel
// close(chan) //關閉一個channel

// 判斷一個channel是否已關閉
//1. 若是channel已經關閉,繼續往它發送數據會致使panic: send on closed channel
//2. 關閉一個已經關閉的channel也會致使panic: close of closed channel
func test2(ch chan int){
    for{
        if value,ok:=<-ch;ok{
            //do somthing
            fmt.Print(value)
        }else{
            break //ok 爲false, 表示channel已經被關閉,退出循環
        }
    }
}

// channel關閉後,仍然能夠從中讀取已發送的數據(使用range沒法實現),讀取完數據後,將讀取到零值,能夠屢次讀取。
func test1(){
    ch:=make(chan int,3)
    ch<-3
    ch<-2
    ch<-1
    close(ch)
    fmt.Print(<-ch)
    fmt.Print(<-ch)
    fmt.Print(<-ch)
    fmt.Print(<-ch)
    fmt.Print(<-ch)
}
相關文章
相關標籤/搜索