Golang并发编程入门:Go与Channel详解
Golang的并发模型是语言设计的亮点。本文解析如何在Go中创建轻量级线程(goroutine),并通过channel机制安全高效地进行线程间通信,最后展示如何利用select实现多任务同步。
使用go关键字开启协程
Golang运行时管理了一种被称为 goroutine 的轻量级线程。与普通操作系统线程相比,它的创建开销极小,同时运行数十万个甚至更多的 goroutine 也是可行的。要启动一个 goroutine,只需在普通函数或方法调用前加上 go 关键字即可。
package main
import (
"fmt"
"time"
)
// 被调用的函数
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
// 异步执行say("world")
go say("world")
// 同步执行say("hello")
say("hello")
}
通过Channel进行通讯
为了避免显式使用互斥锁来保证线程安全,Go推荐直接使用 channel 在不同 goroutine 之间传递数据。channel 是一种带有类型的管道,必须指定其收发值的类型。箭头操作符 <- 表示数据的流向:
- 发送:
ch <- v(将v发送到ch)。 - 接收:
v := <-ch(从ch接收值赋给v)。
发送和接收默认是阻塞的。通过 make 函数可以创建 channel,语法为 make(chan 数据类型)。
实例:分割数组求和
以下代码演示了如何将一个数组拆分为两部分,开启两个 goroutine 并行计算部分和,最后在主线程汇总结果:
package main
import "fmt"
// sum 函数计算数组元素之和,并将结果写入 channel
func sum(a []int, c chan int) {
s := 0
for _, v := range a {
s += v
}
c <- s // 发送结果
}
func main() {
a := []int{7, 2, 8, -9, 4, 0}
// 创建接收 int 的 channel
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)
}
带缓冲的Channel与关闭
channel 可以通过第二个参数设置缓冲区长度。发送时仅在缓冲区满时才阻塞,接收时仅在缓冲区空时才阻塞:
// 创建长度为2的缓冲channel
ch := make(chan int, 2)
ch <- 1
ch <- 2 // 此时不阻塞
fmt.Println(<-ch)
当发送者不再发送数据时,可以调用 close(ch) 关闭通道。注意:严禁向已关闭的 channel 发送值,否则会引发 panic;向已关闭且有缓冲的 channel 接收值时,先取走剩余值,然后均返回该类型的零值。
为了配合循环取值,for range 结构非常实用。它可以自动遍历 channel 的值,直到 channel 被关闭为止:
func fibonacci(n int, c chan int) {
x, y := 0, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
close(c) // 发送完毕后必须关闭
}
func main() {
c := make(chan int)
go fibonacci(10, c)
// 自动遍历直至关闭
for i := range c {
fmt.Println(i)
}
}
利用Select处理多路复用
当一个 goroutine 需要监听多个 channel 通讯时,可以使用 select 语句。它会阻塞直到某个 case 能够运行。如果多个 case 同时满足,则随机选择一个执行。
select 还支持一个可选的 default 分支。如果没有任何 case 准备好,立即执行 default,从而实现非阻塞检查或超时逻辑:
package main
import (
"fmt"
"time"
)
func main() {
tick := time.Tick(100 * time.Millisecond)
boom := time.After(500 * time.Millisecond)
for {
select {
case <-tick:
fmt.Println("tick.")
case <-boom:
fmt.Println("BOOM!")
return
default:
fmt.Println("...")
time.Sleep(50 * time.Millisecond)
}
}
}
总结
掌握 goroutine 的创建以及 channel 的基本收发操作是理解 Go 并发的第一步。合理使用缓冲机制可以提升性能,而 select 则是解决复杂并发协作问题的关键工具。以上就是 Golang 并发编程基础支持的详细内容,更多关于 Golang 并发控制机制的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













