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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM