Go Channel

Channels

概念:通道Channels可被认为是Goroutines通信的管道。

声明:通道零值为nil(没有任何作用),故通道必须使用类似map和slice的方法来定义

 a := make(chan int) 

发送与接收

data := <- a // read from channel a   eg: chan <- //发送数据
a <- data // write to channel a    eg:  <-cjan// 接收数据

一个通道发送、接收数据默认是阻塞的。故一个数据被发送到channel,在发送语句中被阻塞,直到另一个Goroutine

来接收。接收数据也类似。

通道的特性是帮助Goroutines有效地进行通信,而无需像使用其他编程语言中非常常见的显式锁或条件变量。

Channel的理论基础是CSP,Go语言的并发基于CSP模型。不要通过共享内存来通信,要通过通信来共享内存。

1、Channel基础

channel是goroutine之间交互,发一个数据必须有另一个goroutine去接收它。

例子1:channel发数据

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发过去了但是来不及打印出来就结束了
}

类似函数一样,一等公民,可以作为参数也可作为返回值,可以随意的加参数。

注: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
",
            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

2、通过通信来共享内存

  

使用Channel等待任务结束

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

 3、使用Channel实现树的遍历

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

4、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
",
            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

 5、传统同步机制

这里只是一个示例,详情参见https://www.cnblogs.com/ycx95/p/9358739.html 的atomic.go

但是不建议。

原文地址:https://www.cnblogs.com/ycx95/p/9379565.html