Go Channel

 

Channels

概念:通道Channels可被認爲是Goroutines通訊的管道。html

聲明:通道零值爲nil(沒有任何做用),故通道必須使用相似map和slice的方法來定義編程

 a := make(chan int) 併發

發送與接收

data := <- a // read from channel a   eg: chan <- //發送數據
a <- data // write to channel a    eg:  <-cjan// 接收數據

 

一個通道發送、接收數據默認是阻塞的。故一個數據被髮送到channel,在發送語句中被阻塞,直到另外一個Goroutineapp

來接收。接收數據也相似。編程語言

通道的特性是幫助Goroutines有效地進行通訊,而無需像使用其餘編程語言中很是常見的顯式鎖或條件變量。函數

 

Channel的理論基礎是CSP,Go語言的併發基於CSP模型。不要經過共享內存來通訊,要經過通訊來共享內存。atom

一、Channel基礎

channel是goroutine之間交互,發一個數據必須有另外一個goroutine去接收它。spa

例子1:channel發數據3d

func chanDemo(){
    c := make(chan int)
    //這裏是開一個goroutine去接受發送的 若是沒有會報錯
    go func() {
        for {
            n :=  <-c
            fmt.Println(n)
        }
    }()
    c <- 1
    c <- 2
    time.Sleep(time.Millisecond)//①
}
func main() {
    chanDemo() //若是沒有①則這裏輸出是1,由於雖然2發過去了可是來不及打印出來就結束了
}

 

相似函數同樣,一等公民,能夠做爲參數也可做爲返回值,能夠隨意的加參數。code

注:chan<- 說明是用來發送數據的,<-chan說明是用來接收數據的

例子2:channel.go

重點涉及:channel、buffered channel、range   

//打印收到的數據
func worker(id int, c chan int) {
    for n := range c {
        fmt.Printf("Worker %d received %c\n",
            id, n)
    }
}
//建立channel 而後將其返回出去  chan<- int 用來發送數據
func createWorker(id int) chan<- int {
    c := make(chan int)
    go worker(id, c) //裏面的人用來收數據
    return c
}

func chanDemo() {
    var channels [10]chan<- int
    for i := 0; i < 10; i++ {
        channels[i] = createWorker(i)//分發
    }

    for i := 0; i < 10; i++ { //發送小a
        channels[i] <- 'a' + i
    }

    for i := 0; i < 10; i++ {
        channels[i] <- 'A' + i
    }

    time.Sleep(time.Millisecond)
}

func bufferedChannel() {
    c := make(chan int, 3)//channel發送以後必須有人收,這裏緩衝區是3
    go worker(0, c)
    c <- 'a'
    c <- 'b'
    c <- 'c'
    c <- 'd'
    time.Sleep(time.Millisecond)//必需要加這個要否則沒有輸出哦
}
//告訴接收方我發完數據啦
func channelClose() {
    c := make(chan int)
    go worker(0, c)
    c <- 'a'
    c <- 'b'
    c <- 'c'
    c <- 'd'
    close(c)
    time.Sleep(time.Millisecond)
}

func main() {
    fmt.Println("Channel as first-class citizen")
    chanDemo()
    fmt.Println("Buffered channel")
    bufferedChannel()
    fmt.Println("Channel close and range")
    channelClose()
}

 

 輸出是:

Channel as first-class citizen
Worker 0 received a
Worker 1 received b
Worker 2 received c
Worker 3 received d
Worker 4 received e
Worker 5 received f
Worker 6 received g
Worker 7 received h
Worker 8 received i
Worker 8 received I
Worker 9 received j
Worker 9 received J
Worker 0 received A
Worker 1 received B
Worker 2 received C
Worker 3 received D
Worker 4 received E
Worker 5 received F
Worker 6 received G
Worker 7 received H
Buffered channel
Worker 0 received a
Worker 0 received b
Worker 0 received c
Worker 0 received d
Channel close and range
Worker 0 received a
Worker 0 received b
Worker 0 received c
Worker 0 received d

 

二、經過通訊來共享內存

  

使用Channel等待任務結束

func doWork(id int, w worker) {
    for n := range w.in {
        fmt.Printf("Worker %d received %c\n",
            id, n)
        w.done() //告訴別人我打印完啦 發送done
    }
}

type worker struct {
    in   chan int
    done func()
}

func createWorker(
    id int, wg *sync.WaitGroup) worker {
    w := worker{
        in: make(chan int),
        done: func() {
            wg.Done()
        },
    }
    go doWork(id, w)
    return w
}

func chanDemo() {
    var wg sync.WaitGroup   //併發 等待

    var workers [10]worker
    for i := 0; i < 10; i++ {
        workers[i] = createWorker(i, &wg) //10個worker將
    }

    wg.Add(20) //20個任務
    for i, worker := range workers {
        worker.in <- 'a' + i
    }
    for i, worker := range workers {
        worker.in <- 'A' + i
    }

    wg.Wait()
}

func main() {
    chanDemo()
}

 

 三、使用Channel實現樹的遍歷

https://www.cnblogs.com/ycx95/p/9361122.htm

四、CSP模型實現

用select進行調度

例子1:拿不到數據不會報錯,可是會進default,輸出是:No value received

channel中若是會進入非阻塞,能夠用select+default

 

func generator() chan int {
    out := make(chan int)
    go func() {
        i := 0
        for {
            time.Sleep(
                time.Duration(rand.Intn(1500)) *
                    time.Millisecond)
            out <- i
            i++
        }
    }()
    return out
}

func worker(id int, c chan int) {
    for n := range c {
        time.Sleep(time.Second)
        fmt.Printf("Worker %d received %d\n",
            id, n)
    }
}

func createWorker(id int) chan<- int {
    c := make(chan int)
    go worker(id, c)
    return c
}

func main() {
    var c1, c2 = generator(), generator()
    var worker = createWorker(0)

    var values []int
    tm := time.After(10 * time.Second)
    tick := time.Tick(time.Second)
    for {
        var activeWorker chan<- int
        var activeValue int
        if len(values) > 0 {
            activeWorker = worker
            activeValue = values[0]
        }

        select {
        case n := <-c1:
            values = append(values, n)
        case n := <-c2:
            values = append(values, n)
        case activeWorker <- activeValue:
            values = values[1:]

        case <-time.After(800 * time.Millisecond):
            fmt.Println("timeout")
        case <-tick:
            fmt.Println(
                "queue len =", len(values))
        case <-tm:
            fmt.Println("bye")
            return
        }
    }
}

 輸出結果:

queue len = 3
Worker 0 received 0
queue len = 5
Worker 0 received 0
queue len = 5
Worker 0 received 1
queue len = 10
Worker 0 received 1
queue len = 10
Worker 0 received 2
queue len = 11
Worker 0 received 2
queue len = 12
Worker 0 received 3
queue len = 13
Worker 0 received 3
queue len = 14
Worker 0 received 4
bye

Process finished with exit code 0

 

 五、傳統同步機制

這裏只是一個示例,詳情參見https://www.cnblogs.com/ycx95/p/9358739.html 的atomic.go

可是不建議。

相關文章
相關標籤/搜索