Golang中的goroutine并发编程
什么是goroutine
在Golang中,goroutine是一种轻量级的线程实现,可以在一个程序中同时执行多个任务。Goroutine由Go语言的运行时系统调度,并且可以在多核处理器上并行执行。与传统的线程相比,goroutine的创建和销毁开销更小,因此可以高效地支持大量的并发任务。
在Golang中,使用go
关键字可以启动一个新的goroutine。例如:
package main
import (
"fmt"
"time"
)
func sayHello() {
fmt.Println("Hello, goroutine!")
}
func main() {
go sayHello()
time.Sleep(1 * time.Second) // 等待goroutine执行完毕
fmt.Println("Main goroutine exiting.")
}
上面的代码中,sayHello()
函数会在一个新的goroutine中执行,而main
函数则在主goroutine中执行。使用go
关键字启动goroutine后,程序会继续往下执行,不会等待goroutine执行完毕。
goroutine的调度
在Golang中,goroutine的调度由运行时系统自动管理,开发者不需要关心具体的调度细节。Golang的运行时系统使用了类似于工作窃取(work stealing)的调度算法,能够高效地利用多核处理器。
在Golang中,可以通过runtime.GOMAXPROCS(n)
函数设置并发执行的最大CPU核数。默认情况下,Golang会根据当前计算机的CPU核数来设置并发执行的最大核数。
package main
import (
"fmt"
"runtime"
)
func main() {
numCPU := runtime.NumCPU()
fmt.Println("CPU核数:", numCPU)
// 设置并发执行的最大CPU核数为1
runtime.GOMAXPROCS(1)
numCPU = runtime.GOMAXPROCS(0)
fmt.Println("最大CPU核数:", numCPU)
}
上面的代码中,runtime.NumCPU()
函数可以获取当前计算机的CPU核数,runtime.GOMAXPROCS()
函数可以设置并发执行的最大CPU核数。
goroutine间的通信
在Golang中,goroutine之间的通信是通过channel完成的。Channel是一种类型,可以用来在goroutine之间传递数据。Channel可以被用来同步goroutine的执行顺序,也可以被用来传递数据。
package main
import (
"fmt"
)
func produce(ch chan int) {
for i := 0; i < 5; i++ {
ch <- i
}
close(ch)
}
func consume(ch chan int) {
for {
val, ok := <-ch
if !ok {
break
}
fmt.Println("Consumed:", val)
}
}
func main() {
ch := make(chan int)
go produce(ch)
go consume(ch)
fmt.Scanln() // 防止主goroutine提前退出
}
上面的代码中,produce
函数向channel发送数据,consume
函数从channel接收数据。在main
函数中,创建了一个channel,并分别在两个goroutine中调用produce
和consume
函数。
goroutine的同步
在多个goroutine之间进行数据交互时,必须保证数据的一致性。Golang提供了多种同步机制,如sync.WaitGroup
和sync.Mutex
等,来保证goroutine之间的同步。
package main
import (
"fmt"
"sync"
)
var (
count int
lock sync.Mutex
wg sync.WaitGroup
)
func increment() {
defer wg.Done()
lock.Lock()
count++
lock.Unlock()
}
func main() {
for i := 0; i < 1000; i++ {
wg.Add(1)
go increment()
}
wg.Wait()
fmt.Println("Count:", count)
}
上面的代码中,使用sync.Mutex
来保护共享变量count
的访问。通过sync.WaitGroup
来等待所有goroutine执行完毕,保证主goroutine在所有goroutine执行完毕后再退出。
goroutine的异常处理
在Golang中,goroutine中的异常无法被外部捕获,因此需要在goroutine内部进行异常处理。可以使用recover
函数来捕获goroutine中的异常,并在defer函数中进行处理。
package main
import (
"fmt"
)
func div(a, b int) {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered:", r)
}
}()
if b == 0 {
panic("division by zero")
}
fmt.Println("Result:", a/b)
}
func main() {
go div(10, 0)
fmt.Scanln() // 防止主goroutine提前退出
}
上面的代码中,div
函数在除法运算时可能会引发异常,使用recover
函数来捕获异常并处理。在异常发生时,程序会打印出异常信息并继续执行。
总结
通过本文的介绍,我们了解了Golang中goroutine的基本概念和用法。Goroutine是Golang并发编程的重要组成部分,可以高效地支持大规模并发任务。在实际开发中,合理地使用goroutine可以提高程序的性能和响应速度,同时注意处理好goroutine间的通信和同步,确保程序的正确性和稳定性。