zoukankan      html  css  js  c++  java
  • goroute理解

    1.协程的作用域
    package main

    import (
    "fmt"
    )

    func test(a string){
    fmt.Println("我是:",a)
    }


    func main() {
    fmt.Println(111111)
    go test("并发")
    test("普通")
    }
        执行以下的代码,等到的结果是:

        

    其实这里面有一个问题就是,系统中每一个进程最少是有一个线程的,而协程也是作用在进程内的,而go多进程的实现,就是利用轻量级的协程来实现的;(协程其实就是异步+回调的方式)所以当程序全部运行结束的时候,协程还没有走完,最终没有输出结果.
    主函数返回时,所有的goroutine都会被直接打断,程序退出。除了从主函数退出或者直接终止程序之外,没有其它的编程方法能够让一个goroutine来打断另一个的执行,但是之后可以看到一种方式来实现这个目的,通过goroutine之间的通信来让一个goroutine请求其它的goroutine,并让被请求的goroutine自行结束执行。

    在明白为什么程序没有输出结果以后,我们也找到了相应的对策:
    package main

    import (
    "fmt"
    "time"
    )

    func test(a string){
    fmt.Println("我是:",a)
    }


    func main() {
    fmt.Println(111111)
    go test("并发")
    test("普通")
    time.Sleep(1)
    }
        time.Sleep(1) 程序沉睡1s,让我们的协程有充分的时间去做异步回调等等...

        输出的结果为:

                


    2.协程的使用
    package main

    import (
    "fmt"
    "time"
    )

    func loop(i int,b string) {
    fmt.Println("我是:",b,i)
    }

    func main() {
    for i:=0;i<10 ;i++ {
    loop(i,"单线程")
    }
    for i:=0;i<10 ;i++ {
    go loop(i,"多线程")
    }
    time.Sleep(1)
    }
        结果为:    

                

    这里我们可以注意到,首先第一个for是单线程的,所以就是按顺序执行,而下一个for使用到了goroute,基本可以认为是谁快谁输出,所以执行的顺序是无序的;


    3.Channels的基本语法

    无缓存的错误写法:
    package main

    import "fmt"

    func main() {
    c:=make(chan int)
    c<-2
    fmt.Println("接收成功:",<-c)
    }
    这里是会报错的,本来没有消耗,以及阻塞了,再加一个便会报错


    有缓存的错误写法:

    package main

    import "fmt"

    func main() {
    c:=make(chan int,1)
    c<-2
    fmt.Println("接收成功:",<-c)
    }
    这里不会报错而是会返回  接收成功: 2


    4.Channels间的通讯
    package main

    import (
    "time"
    "fmt"
    )

    func write(c chan int) {
    for i := 0; i < 10; i++ {
    c <- i
    }
    }

    func read(c chan int) {
    for {
    fmt.Println("go:",<-c)
    time.Sleep(1)
    }
    }
    func readgo(c chan int) {
    for v := range c{
    fmt.Println("并发通信:",v)
    }
    }

    func main() {
    c := make(chan int)
    go write(c)
    go read(c)
    go readgo(c)
    time.Sleep(2)
    }


    结果返回:

                    

    在上面的例子中我们可以看到,channels在goroute中的通信是同时进行的,(如果是单线程的顺序执行进行,go:1是不可能在中间的)也就是说go write(c)/go read(c)/go readgo(c)这3个函数是同时进行的,有读有写才不会让goroute阻塞

    5.Channels的关闭
    package main

    import "fmt"

    func main() {
    a := make(chan int, 15)
    for i := 0; i < 10; i++ {
    a <- i
    }

    close(a)

    for {
    if v, ok := <-a; !ok {
    fmt.Println("不存在")
    break
    } else {
    fmt.Println(v)
    }
    }

    }
            结果返回:

                        

    例子中的channels是a,有本来的15个缓存位置,但是只是用了10个,如何没有关闭,系统则会报错
    记住应该在生产者的地方关闭 channel,而不是消费的地方去关闭它,这样容易引起 panic
    6.利用Channels的通信关闭goroute
    package main

    import (
    "fmt"
    "strconv"
    )

    func add(c chan string, i int) {

    fmt.Println("我的编号是:", i)
    c <- "天王盖地虎"+strconv.Itoa(i)
    }

    func main() {
    c := make(chan string)
    for i := 0; i < 10; i++ {
    go add(c, i)
    }
    for i := 0; i < 10; i++ {
    <-c
    }
    }
            结果返回:

                        
    代码的逻辑是,先创建了10个线程,并在管道中建立通信,完后才会提取c中的数据,最后提取完后,结束进程.
    代码分析:  go add(c,i)在执行的时候是异步的,所以是不会占用主线程,这时channels中会形成阻塞,当执行<-c的处理时又会开始消耗channels,此时的for是占用线程的,当全部执行完for循环时,意味着go add(c,i)也就全部执行结束,    最终提取完成,结束进程


    假设1:
    代码先提取channels中的数据,后添加,这样的运行结果是panic的,原因是channels的c中一直没有数据,所以提取会直接报错
    for i := 0; i < 10; i++ {
    <-c
    }
    for i := 0; i < 10; i++ {
    go add(c, i)
    }


    7.多个Channels的利用
    package main

    import (
    "fmt"
    "time"
    )

    func add(job chan int, c chan bool, i int) {
    for v := range job {
    fmt.Println("序号:", i, "我的工作是:", v)
    time.Sleep(time.Second)
    c <- true
    }

    }

    func main() {
    c := make(chan bool)
    job := make(chan int,10)
    for i := 0; i < 3; i++ {
    go add(job, c, i)
    }
    for i := 0; i < 10; i++ {
    job<-i
    }
    close(job)
    for i := 0; i < 10; i++ {
    <-c
    }
    }
                结果返回:

                    

    这里面需要注意的是:在add()这个函数里面有time.Sleep(time.Second)(沉睡一秒)的条件,所以我们可以更加的直观查看代码的执行;
    代码分析:创建c和job两个管道,创建3条线程并发执行add()函数,job里面插入数据后关闭,最后读取管道c的数据,最终结束
    add()函数中创建了10条job信息,而线程只有3个,执行完一次只能是程序沉睡1秒,最后才有了这样的结果
    ---------------------
    作者:yí無所冇
    来源:CSDN
    原文:https://blog.csdn.net/feiwutudou/article/details/80607920
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    流程控制-分支结构
    nginx配置详解
    git/github初级使用
    svn基本使用详情
    windows下搭建SVN服务器
    Linux下搭建svn服务器
    构建源码
    swipe实现app滑动效果
    app测试专项(摘抄自网络)
    新版本覆盖安装升级后,微信登陆提示获取openid失败
  • 原文地址:https://www.cnblogs.com/journey-mk5/p/9925621.html
Copyright © 2011-2022 走看看