golanggoroutine是Go语言中的一种并发机制,它可以让程序员通过轻量级线程(或称协程)来编写高效的并发程序。Go语言因此变得越来越受到程序员的青睐,尤其在分布式系统、网络编程、高并发场景下表现极为出色。
一、让并发变得更加简单
相比于其他编程语言来说,golanggoroutine是一种受欢迎的并发解决方案,最初设计初衷就是让并发编程变得更简单,更易于上手。以往,程序员需要手动创建线程,并在其中使用锁来避免资源竞争导致的问题。这是一项相对困难的任务,而且还容易出错。 在Go中,golanggoroutine是一种轻量级的线程,Go语言运行时(runtime)会在适当的时候将goroutine调度到不同的线程中执行。而这一切都是由Go语言运行时管理的,对于使用者来说,只需要将程序模块打包成golanggoroutine,就可以更方便地进行并发编程。 下面是样例代码:
func countNumbers() {
for i := 1; i < 10; i++ {
fmt.Println(i)
time.Sleep(time.Millisecond * 500)
}
}
func countCharacters() {
for char := 'a'; char <= 'z'; char++ {
fmt.Printf("%c ", char)
time.Sleep(time.Millisecond * 500)
}
}
func main() {
go countNumbers()
go countCharacters()
time.Sleep(time.Second * 5)
fmt.Println("Done")
}
在上面的代码示例中,countNumbers和countCharacters分别代表了两个goroutine。调用go关键字可以轻松地实现并发执行两个goroutine的操作。可以看到,相比于其他语言的线程实现,代码的编写量要少得多。并且,使用goroutine也可以避免对锁的依赖,使得程序不容易出现死锁等问题。
二、启动goroutine
启动goroutine非常简单,只需要在函数调用前加上关键词go即可。Go语言运行时会启动一个新的goroutine,函数也会在独立的goroutine中运行。 下面是样例代码:
package main
import (
"fmt"
)
func worker() {
fmt.Println("Worker")
}
func main() {
go worker()
fmt.Println("Main")
// sleep to show output
time.Sleep(time.Second)
}
在上面的代码示例中,可以看到,在worker函数调用前,加了go关键字。在程序运行时,worker函数将会在独立的goroutine中执行,和main函数同时运行。可以看到,在console中打印出了Worker和Main两个字符串。
三、goroutine中使用channel进行通信
除了使用goroutine外,Go语言中的另一个重要特性是channel。channel是一种数据类型,用于在不同的goroutine之间传递消息。 下面是样例代码:
package main
import (
"fmt"
)
func main() {
ch := make(chan int)
go func() {
ch <- 1
ch <- 2
ch <- 3
ch <- 4
close(ch)
}()
for n := range ch {
fmt.Println(n)
}
}
在上面的代码示例中,使用make函数创建了一个整数类型的channel。启动一个goroutine并将整数类型的值1-4写入channel中,最后关闭channel。在主函数中,使用for循环和range迭代器接收channel中的值,然后将它们打印到控制台中。
四、使用select语句管理channel
select语句使 goroutine 执行期间可以等待多个channel 上的事件。select语句的响应方式类似于switch语句,它会等待其中之一条件成立时执行对应的语句块。 下面是样例代码:
package main
import (
"fmt"
"time"
)
func worker1(messages chan string) {
for i := 0; ; i++ {
messages <- "worker1: " + fmt.Sprintf("%d", i)
time.Sleep(time.Second)
}
}
func worker2(messages chan string) {
for i := 0; ; i++ {
messages <- "worker2: " + fmt.Sprintf("%d", i)
time.Sleep(time.Second * 2)
}
}
func main() {
messages := make(chan string)
go worker1(messages)
go worker2(messages)
for {
select {
case message1 := <-messages:
fmt.Println(message1)
case message2 := <-messages:
fmt.Println(message2)
case <-time.After(time.Second * 5):
fmt.Println("timeout")
return
}
}
}
在上面的代码示例中,启动了两个goroutine。使用make函数创建一个通道,并将两个goroutine中的结果写入该通道。主函数中使用select语句等待message1和message2从通道messages上被同时接收到。其中第三个case用time.After模拟5秒超时,当这个case被选择时,程序将结束并退出。
五、golanggoroutine的性能
由于goroutine是轻量级线程,创建和销毁时开销都非常小,所以它们可以被迅速地创建和销毁。此外,Go语言运行时在多个goroutine之间进行智能调度,可以让多个goroutine在单个或少量线程上并发运行,不会出现线程的频繁切换带来的性能开销。 下面是样例代码:
package main
import "fmt"
func sum(a []int, c chan int) {
sum := 0
for _, v := range a {
sum += v
}
c <- sum
}
func main() {
a := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(a[:len(a)/2], c)
go sum(a[len(a)/2:], c)
x, y := <-c, <-c
fmt.Println(x, y, x+y)
}
在上面的代码示例中,采用了两个goroutine进行数组的并行求和。在最终结果被求出的时候,只需要进行一次channel的读取操作,使得程序具有比较快的运行速度。
结语
golanggoroutine是Go语言中的一种重要并发机制,该特性具有轻量、简单、易用、性能高等优点。在分布式系统、网络编程、高并发场景下表现尤为突出。借助于goroutine和channel, Go语言可以更加容易地实现并发编程。