Golang中的Channel

goroutine和goroutine之間通過channel通信

channel的創建

1 使用make創建

channel創建可以用make方法,如:

c := make(chan int)

此外,如果make方法第二個參數多寫一個數字,則表示對channel作buffer緩衝:

// 表示channel中每接收3個後再給接收者
c := make(chan int, 3) 

2 使用var創建

不用make的話,也可以用var:

var c chan int  // 此時c爲nil

如果創建channel數組,則寫法如下:

var channels [10]chan int

channel的傳與收

往創建好的channel中傳數據使用<-表示,如:

c <- 1
c <- 2

注意:往channel傳數據後必須要有接收者,否則程序運行會報deadlock

較完整的示例片段如下:

// 創建一個channel
c := make(chan int)

// 起一個goroutine,用來接收channel中的數據並打印
go func() {
    for {
        n := <-c
        fmt.Println(n)
    }
}()

// 向channel中傳數據
c <- 1
c <- 2

// 這裏加個sleep,是爲了避免channel中的數據還沒打印,main函數就退出了
time.Sleep(time.Millisecond)

channel可作參數和返回值

channel可以作爲參數傳遞,也可以作返回值。

需要注意的是,channel作爲收數據還是發數據,寫法是不一樣的。
chan<- int 和<-chan int這兩個是有區別的:

  • chan<- int表示send only,只能往裏發
  • <-chan int表示revice only,只能從裏收

channel的close

發送方可以使用close©方法來關閉channel,以告訴接收方沒有新數據了。
channel關閉後,接收者還能接收數據不會報錯,不過接收到的是對應類型的零值

如何判斷channel是否關閉?
方法(1): 通過if判斷

n, ok := <-c 
if !ok {
    fmt.Println("channel 沒有數據了")
    break
}

方法(2): 通過range

for n := range c {
    ...
}

使用sync.WaitGroup等待goroutine任務結束

之前使用time.Sleep(time.Millisecond)的方式來讓main函數等待任務完成,但是不太好。
golang中提供了sync.WaitGroup方法來實現這個功能

例:

package main

import (
	"fmt"
	"sync"
)

func doWork(id int,
	w worker) {
	for n := range w.in {
		fmt.Printf("Worker %d received %c\n",
			id, n)
		w.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)
	}

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

	wg.Wait()
}

func main() {
	chanDemo()
}

select的使用

例:

var c1, c2 chan int
select {
case n := <-c1:
    fmt.Println("received from c1:", n)
case n := <-c2:
    fmt.Println("received from c2:", n)
default:
    fmt.Println("no value received")
}

注意:通過var方式聲明的c1和c2默認是nil,但用select方式並不會報錯,因爲有default兜底。
如果這裏沒有default,程序則會報deadlock,因爲這裏只有接收者,沒有channel的發送者

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章