Golang如何在分隔的独立线程中运行指定的函数?通常可以使用go关键字启动一个新的执行线程goroutine。它会在一个新创建的goroutine中运行指定的函数。一个程序中的所有goroutine共享相同的地址空间。
例如以下代码:
go list.Sort() //通过goroutine并发执行list.Sort
下面的程序将打印“Hello from main goroutine”;也可能打印“Hello from another goroutine”,结果取决于哪个goroutine先完成。
func main() {
go fmt.Println("Hello from another goroutine")
fmt.Println("Hello from main goroutine")
//执行到这里时所有程序终止
//所有活动的goroutine线程会被销毁
}
下面的代码很可能同时打印“Hello from main goroutine”和“Hello from another goroutine”。并且可能是按任何顺序打印的。
func main() {
go fmt.Println("Hello from another goroutine")
fmt.Println("Hello from main goroutine")
time.Sleep(time.Second) //休眠几秒等待另一个goroutine完成
}
下面是一个比较实际的例子,代码中定义了一个使用并发性来延迟事件的函数。
// Publish在给定的时间过期后将文本信息打印到标准输出中
// 由于使用了goroutine函数,本身不会阻塞而是会立刻返回
func Publish(text string, delay time.Duration) {
go func() {
time.Sleep(delay)
fmt.Println("BREAKING NEWS:", text)
}() //注意括号,这里调用的是匿名函数
}
接下来调用Publish函数:
func main() {
Publish("Agoroutine starts a new thread.", 5*time.Second)
fmt.Println("Let’s hope the news will published before I leave.")
//等待goroutine中的消息发布
time.Sleep(10 * time.Second)
fmt.Println("Ten seconds later: I’m leaving now.")
}
程序很可能按给定的顺序打印以下三行,每一行之间约有5秒的间隔。
$ go run publish1.go
Let’s hope the news will published before I leave.
BREAKING NEWS: A goroutine starts a new thread.
Ten seconds later: I’m leaving now.
通常不建议通过休眠来协调线程使它们彼此等待。Go的主要同步方法仍然是使用通道。
结论
goroutine是轻量级的,成本仅比堆栈空间的分配高一点点。栈开始很小,然后根据需要分配和释放堆存储空间,从而不断增长。线程内部的goroutine就像协程一样,会在多个操作系统线程之间进行多路复用。如果一个goroutine阻塞了一个OS线程,例如等待输入,那么这个线程中的其他goroutine将迁移,以便它们可以继续运行。
问题思考:
为什么要基于CSP的思想构建并发性?
随着时间的推移,对于并发性和多线程编程非常困难的这样一种认知似乎越来越普遍。主要原因一部分是由于复杂的设计,另一部分是由于过分强调底层细节(如互斥、条件变量和内存屏障)。更高级的接口支持更简单的代码,即使仍然存在互斥锁之类的东西。
为并发性提供高级语言支持的最成功的模型之一来自于Hoare的通信顺序流程模型(CSP)。Occam和Erlang是源于CSP的两种著名语言。Go的并发原语来自于家族树的另一部分,其主要特点是将通道(Channel)作为语言中处理并发的主要概念。几种早期语言的经验表明,CSP模型非常适合类似Go这样的过程语言。
为什么使用Goroutine而不是thread?
Goroutine使得并发编程更易于开发者使用。这个理念其实已经存在很长一段时间了,它是将独立执行的函数(协程)多路复用到一组线程上。当协同程序(即goroutine)阻塞时,例如调用阻塞IO系统,运行时自动将同一操作系统线程上的其他协同程序移动到不同的、可运行的线程,这样它们就不会被阻塞。重点是所有过程对开发者透明。goroutines的性能非常高,除了堆栈的内存(只有几kb)之外,它们几乎没有开销。
为了使堆栈更小,Go的运行时使用可调整大小的有界堆栈。一个新生产的goroutine被赋予几个kb,一般来说这点内存已经足够了。如果需要更多内存,则运行时会自动增加(或减少)存储堆栈的内存,从而允许许多goroutine驻留在有限的内存中。因此在同一个地址空间中创建成千上万的goroutine是可行的。如果goroutine只是线程,那么系统资源不足以支撑大规模的线程数量,很快就会耗尽。
原文作者:yourbasic.org 译者:江玮