概念
- 协程与传统的系统级线程和进程相比,协程的最大优势在于其“轻量级”,可以轻松创建上百万个而不会导致系统资源衰竭,而线程和进程通常最多也不能超过1万个。这也是协程也叫轻量级线程的原因。多数语言不支持携程,而是通过库的方式支持,这样如果调用同一个io如本地文件读写都会阻塞其他并发执行轻量级线程。
- go在语言级别支持协程,叫goroutine。
- go 关键词后面的语句会以一个新的线程去运行。
goroutine
func main() {
go fmt.Println("Hello from another goroutine”)// 线程
fmt.Println("Hello from main goroutine")
// 至此,程序运行结束,
// 所有活跃的goroutine被杀死
}
// 函数Publish在给定时间过期后打印text字符串到标准输出
// 该函数并不会阻塞而是立即返回
func Publish(text string, delay time.Duration) {
go func() {
time.Sleep(delay)
fmt.Println("BREAKING NEWS:", text)
}() // 注意这里的括号。必须调用匿名函数
}
func main() {
Publish("A goroutine starts a new thread of execution.", 5*time.Second)
fmt.Println("Let’s hope the news will published before I leave.")
// 等待发布新闻
time.Sleep(10 * time.Second)
fmt.Println("Ten seconds later: I’m leaving now.")
}
输出:
Let’s hope the news will published before I leave.
BREAKING NEWS: A goroutine starts a new thread of execution.
Ten seconds later: I’m leaving now.
channel(通道)
-
channel是Go语言在语言级别提供的goroutine间的通信方式。
-
channel是类型相关的。也就是说,一个channel只能传递一种类型的值,这个类型需要在声明channel时指定。
-
声明格式:var chanName chan ElementType
通道发送数据的格式
通道变量 <- 值
通道接收数据
- 通道接收同样使用
<-
操作符 - 阻塞接收数据,阻塞模式接收数据时,将接收变量作为
<-
操作符的左值data := <-ch
- 非阻塞接收数据,
data, ok := <-ch
data:表示接收到的数据。 ok:表示是否接收到数据。 - 接收任意数据,忽略接收的数据
<-ch
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // 把 sum 发送到通道 c
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
c := make(chan int)
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x, y := <-c, <-c // 从通道 c 中接收
fmt.Println(x, y, x+y)
}
输出结果为:
-5 17 12
注意:默认情况下,通道是不带缓冲区的。发送端发送数据,同时必须又接收端相应的接收数据。
通道缓冲区
道可以设置缓冲区,通过 make 的第二个参数指定缓冲区大小 :ch := make(chan int, 100)
func main() {
// 这里我们定义了一个可以存储整数类型的带缓冲通道
// 缓冲区大小为2
ch := make(chan int, 2)
// 因为 ch 是带缓冲的通道,我们可以同时发送两个数据
// 而不用立刻需要去同步读取数据
ch <- 1
ch <- 2
// 获取这两个数据
fmt.Println(<-ch)
fmt.Println(<-ch)
}
输出结果为:
1
2