go 语言并发机制 goroutine 初探

shihao · · 4011 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

go 语言的一个很大的优势就是可以方便地编写并发程序。go 语言内置了 goroutine 机制。这是一种类似 coroutaine(协程) 的东西。但是又不完全相同。

比如这个例子:


package main

import (
	"fmt"
	"strconv"
)

func main() {
	ch := make(chan int)
	task("A", ch)
	task("B", ch)
	fmt.Printf("begin\n")
	<-ch
	<-ch
}
func task(name string, ch chan int) {
	go func() {
		i := 1
		for {
			fmt.Printf("%s %d\n", name, i)
			i++
		}
		ch <- 1
	}()
}

运行以后,发现会 A B 两个 goroutine 会交替执行,并不像传统的协程需要手动 schedule 。看起来很神奇。

稍稍改一下代码,把

fmt.Printf("%s %d\n", name, i)

改成

print(name + " " + strconv.Itoa(i) + "\n")

再看看。神奇的效果消失了,只有 A 被运行。

那么 fmt.Printf 和 print 有什么差别,导致了这个结果呢?

大致翻了一下 go 的代码,看出 go 语言在对 c lib 的包装上用了个 cgo 的方式。而在通过 cgo 调用 c 库的时候,会在调用时自动 schedule 切换走,在调用结束的时候再返回。这两个结果的差异就在于,fmt.Printf 是通过 cgo 包装的,而 print 则是原生实现的。所以在调用 fmt.Printf 的时候,就自动实现了调度。

传统的 coroutaine 在访问网络、数据库等 io 操作时仍然会阻塞,失去并发能力,所以通常需要结合异步 io 来实现。而现有的库如果本身未提供异步功能,就很难办,往往需要重新实现。而且,即使有异步 io 功能,也需要额外的开发,才能在表现上和以往顺序程序相同的方式。

go 语言的这种实现方式很好的解决了这个问题,可以充分利用现有的大量 c 库来包装。

同时,也还是可以使用 runtime.Gosched() 来手动调度。在运算量大的场景下,也还是必要的。

在使用 print 的例子里,如果使用 runtime.GOMAXPROCS(2),又可以重新并行起来。这时,两个 goroutine 是在两个独立的线程中运行的。这又是 goroutine 和协程的一个不同点。不过启用多个线程并不见得能让程序更快。因为跨线程的上下文切换代价也是很大的。在上面那个简单的例子里,还会让程序变得更慢。降低 上下文切换开销也是协程的一个优势所在。


有疑问加站长微信联系(非本文作者)

本文来自:博客园

感谢作者:shihao

查看原文:go 语言并发机制 goroutine 初探

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

4011 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传