zoukankan      html  css  js  c++  java
  • 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

    但是不建议。

  • 相关阅读:
    第八章:简单之美——布尔代数和搜索引擎的索引
    第六章:信息的度量和作用
    第五章:隐马尔可夫模型
    第四章谈谈中文分词
    第二章:自然语言处理———从规则到统计
    转:中文分词算法笔记
    NLTK之WordNet 接口【转】
    sentiwordnet的简单使用
    20169202 2016-2017-2 《移动平台开发实践》实验总结--七周
    20169202 2016-2017-2《移动平台》第七周学习总结
  • 原文地址:https://www.cnblogs.com/ycx95/p/9379565.html
Copyright © 2011-2022 走看看