zoukankan      html  css  js  c++  java
  • go语言之并发编程一

    Go语言最大的优势就在于并发编程。Go语言的关键字go就是开启并发编程也就是goroutine的唯一途径。一条go语句以为着一个函数或方法的并发执行。Go语句是由go关键字和表达式组成。比如下面的这种

    go println(“hello go!”)

    如果go关键字后面的是针对匿名函数的调用方式,那么go语句就像这样:

    go func(){

            println(“hello go!”)

    }()

    无论是否需要传递参数给匿名函数,都不要忘了最后的那对圆括号,它们代表了对函数的调用行为。

     

    当go语句执行的时候,其中的go函数会被单独放入一个goroutine中,在这之后,该go函数的执行会独立于当前的goroutine运行。位于go语句后面的那些语句并不会等到前者的go函数被执行完成后才执行。甚至在该go函数真正执行以前,运行时系统可能就已经开始执行后面的语句了。也就是说go函数并发执行,但谁先谁后并不确定。

     

    那么对于go函数的执行返回结果是如何处理的呢,返回的结果值会在其执行完成时被丢弃,也就是说,即使它们返回了结果值,也不会产生任何意义。如果想把go函数的计算结果传递给其他程序的话,就需要用到channel。

     

    来看个实际的例子:

    func main(){

            go println("hello go!")

    }

    上面的这个代码,我们预期出现打印hello go。但是实际上没有任何打印结果出现。原因就在于运行时系统会并发的执行go函数。运行时系统会使用一个G封装go函数并把它放到可运行的G队列中。但是至于这个新的G什么时候执行,就要看调度器的调度情况了。在上面的例子中,一旦main函数执行结束,就意味着该go程序运行的结束。但是新的G还没来得及执行。由于我们无法掌控并发执行的先后顺序,因此必须通过额外的手段去控制。

    比如像下面一样,在结尾睡眠一秒钟。

    func main(){

            go println("hello go!")

            time.Sleep(time.Second)

    }

    函数time.Sleep的作用是让调用它的goroutine暂停一段时间,也就是进入Gwaiting状态。但是这样的做法并不保险,因为有可能前面的go语句执行时间会超过1秒钟,而这个执行时间也是我们无法控制的。正确的做法是采用runtime.Gosched()。runtime.Gosched()函数的作用是暂停当前的G,好让其他的G有机会运行。但是对于复杂的情况,runtime.Gosched函数也不会适用。最好的办法就是用channel。下面来看下复杂的例子:

    func main(){

            name:="Eric"

            go func(){

                     fmt.Println("hello,%s! ",name)

            }()

            name="Harry"

            time.Sleep(time.Millisecond)

    }

    在这个例子中,究竟是打印hello, Eric还是hello Harry呢。多数情况是打印后者。因为在给name赋值给Harry的时候,go函数还没执行。如果我们把name="Harry"放在time.Sleep()之后,那么就会打印hello Eric。

     

    如果我要同时问候多个人,程序如下

    func main(){

            names:=[]string{"Eric","Harry","Robert","Jim","Mark"}

            for _,name:=range names{

                     go func(){

                             fmt.Println("hello",name)

                     }()

            }

            time.Sleep(time.Millisecond)

    }

    打印结果如下:

    hello Mark

    hello Mark

    hello Mark

    hello Mark

    hello Mark

    原因在于并发的执行的5个go函数,name的值都是Mark。这是因为它们都是在for语句执行完之后才执行的。而name在这时已经被赋值给了Mark。在for循环中添加一个time.Sleep(time.Millisecond)就可以避免这个问题

    func main(){

            names:=[]string{"Eric","Harry","Robert","Jim","Mark"}

            for _,name:=range names{

                     go func(){

                             fmt.Println("hello",name)

                     }()

                     time.Sleep(time.Millisecond)

            }

            time.Sleep(time.Millisecond)

    }

    执行结果:

    hello Eric

    hello Harry

    hello Robert

    hello Jim

    hello Mark

    对于这种情况,我们是否可以每次在name被赋值的时候,都传递给go函数来规避前面的问题呢?答案是肯定的。代码如下。Func使用参数who, 每次name被赋值的时候, 对应的值都会被传递给who这个参数。因此当go函数执行的时候,就是对应的名字,不会出现重复

    func main(){

            names:=[]string{"Eric","Harry","Robert","Jim","Mark"}

            for _,name:=range names{

                     go func(who string){

                             fmt.Println("hello",who)

                     }(name)

            }

            time.Sleep(time.Millisecond)

    }

  • 相关阅读:
    推荐体系算法总结
    Springboot 多模块调用,找不到注入的类
    LRU算法
    在SQLServer中连接另一个SQLServer库数据,在Oracle中连接另一个Oracle库数据,在SQL Server中连接Oracle数据,在Oracle中连接SQL Server数据
    用C#实现木马程序
    CSS filter 滤镜可视化配置
    微信小程序农历日期选择器 lunarpicker
    ms Sql 数据库出现 “提供的统计信息流已损坏”的解决办法。
    自制《要塞:十字军东征》无限金钱修改器
    c#+Winform实现自定义的“复制、粘贴”右键快捷菜单,多个控件共享使用一个右键菜单。
  • 原文地址:https://www.cnblogs.com/zhanghongfeng/p/10036418.html
Copyright © 2011-2022 走看看