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!\n",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)

}

go语言之并发编程一的更多相关文章

  1. Task C# 多线程和异步模型 TPL模型 【C#】43. TPL基础——Task初步 22 C# 第十八章 TPL 并行编程 TPL 和传统 .NET 异步编程一 Task.Delay() 和 Thread.Sleep() 区别

    Task C# 多线程和异步模型 TPL模型   Task,异步,多线程简单总结 1,如何把一个异步封装为Task异步 Task.Factory.FromAsync 对老的一些异步模型封装为Task ...

  2. Python核心技术与实战——十七|Python并发编程之Futures

    不论是哪一种语言,并发编程都是一项非常重要的技巧.比如我们上一章用的爬虫,就被广泛用在工业的各个领域.我们每天在各个网站.App上获取的新闻信息,很大一部分都是通过并发编程版本的爬虫获得的. 正确并合 ...

  3. Java并发编程之CAS二源码追根溯源

    Java并发编程之CAS二源码追根溯源 在上一篇文章中,我们知道了什么是CAS以及CAS的执行流程,在本篇文章中,我们将跟着源码一步一步的查看CAS最底层实现原理. 本篇是<凯哥(凯哥Java: ...

  4. 并发编程之:JMM

    并发编程之:JMM 大家好,我是小黑,一个在互联网苟且偷生的农民工. 上一期给大家分享了关于Java中线程相关的一些基础知识.在关于线程终止的例子中,第一个方法讲到要想终止一个线程,可以使用标志位的方 ...

  5. [转载]并发编程之Operation Queue和GCD

    并发编程之Operation Queue http://www.cocoachina.com/applenews/devnews/2013/1210/7506.html 随着移动设备的更新换代,移动设 ...

  6. Java并发编程之CAS

    CAS(Compare and swap)比较和替换是设计并发算法时用到的一种技术.简单来说,比较和替换是使用一个期望值和一个变量的当前值进行比较,如果当前变量的值与我们期望的值相等,就使用一个新值替 ...

  7. Xcode中C、C++、Object-C3种语言的混编

    转自: http://hi.baidu.com/onejw/item/f34390c997cdc226a1b50ae http://www.cocoachina.com/ask/questions/s ...

  8. 并发编程之wait()、notify()

    前面的并发编程之volatile中我们用程序模拟了一个场景:在main方法中开启两个线程,其中一个线程t1往list里循环添加元素,另一个线程t2监听list中的size,当size等于5时,t2线程 ...

  9. Go语言的并发

    一.Go语言中Goroutine的基本原理 Go语言里的并发指的是能让某个函数独立于其他函数运行的能力. Go语言的goroutine是一个独立的工作单元, Go 语言的并发同步模型来自一个叫作通信顺 ...

随机推荐

  1. Node.js 替换文档内容

    server.js代码: var http=require('http'); var fs=require('fs'); var server=http.createServer(function(r ...

  2. Android学习(十一) File文件操作

    File类 1.获取当前应用程序的目录: this.getFilesDir();                                             //获取当前应用程序的数据目录 ...

  3. python——变量作用域及嵌套作用域

    ----------------------------------------------------------------------------- 前言-------------------- ...

  4. js中数组的合并和对象的合并

    1 数组合并 1.1 concat 方法 var a = [1,2,3], b = [4,5,6]; var c = a.concat(b); console.log(c);// 1,2,3,4,5, ...

  5. Hbase 认识及其作用

    HBase出现的原因: HBase作为面向列的数据库运行在HDFS之上,HDFS缺乏随即读写操作,HBase正是为此而出现.HBase以Google BigTable为蓝本,以键值对的形式存储.项目的 ...

  6. Android下的Handler

    coder是没必要重复造轮子的,写博客亦如此.因为工作忙,学的东西比较多,没法自己来写博客.自己想了个思路就是,把别人的技术精华拿过来,从简到难,慢慢学习.最后提炼,得到自己想学的东西即可,等有时间了 ...

  7. python常见面试题(一)

    1.Python是如何进行内存管理的? 答:从三个方面来说,一对象的引用计数机制,二垃圾回收机制,三内存池机制 一.对象的引用计数机制 Python内部使用引用计数,来保持追踪内存中的对象,所有对象都 ...

  8. C# 系统应用之注冊表使用具体解释

    在平时做项目时,我们有时会遇到注冊表的操作,比如前面我们须要获取IE浏览器地址栏的信息.获取"我的电脑"地址栏输入的目录信息.USB近期使用信息等.注冊表项是注冊表的基本组织单位, ...

  9. 自己动手开发IOC容器

    前两天写简历.写了一句:精通Spring IoC容器.怎么个精通法?还是自己动手写个IOC容器吧. 什么是IoC(Inversion of Control)?什么是DI(Dependency Inje ...

  10. python easy install时,使用aliyun阿里云镜像提示主机名不匹配的问题

    因网络问题,因此设置 easy_install 使用阿里云的源, ## 更新 easy_install 源 tee ~/.pydistutils.cfg <<-'EOF' [easy_in ...