go语言有一个很重要的特性就是 goroutine, 我们可以使用 goroutine 结合 channel 来开发并发程序。
1、goroutine简介
goroutine是go语言中的并发执行单元,我们可以将多个任务分别放在多个 goroutine 中,参见下文案例:
package main
import "fmt"
func hello() {
fmt.Println("Hello World!!!")
}
func main() {
go hello() // 启动goroutine
fmt.Println("Bye!!!")
var input string
fmt.Scanln(&input)
}
多次运行程序结果如下:
图片.png
以上栗子中有几点说明:
- 使用go关键字可以启动一个goroutine运行,go需要加在函数调用名称的前面。
- main函数也在独立的一个goroutine中运行。
- 每次执行的输出结果顺序可能是不同的,因为不同的goroutine执行的时间可能不同。
2、channel简介
在上个例子中fmt.Scanln(&input)
用于阻塞main函数goroutine的运行,知道获得一个输入后,程序终止,主要是为了展示hello函数的运行,否则当程序输出Bye!!!
,main程序自动结束,将看不到hello函数的输出。
go语言提供channel机制给goroutine进行通信:
(1)channeld的创建
ch1 := make(chan int) //创建无缓冲的channel,channel的元素类型为int
ch2 :=make(chan int, 2) //创建有缓冲的channel,channel的元素类型为int,容量为2个元素
channel 使用 make 函数来创建,它是一种引用类型。
(2)向 channel 中读写数据
通道有两个主要操作:发送(send)和接收(receive),两者统称为通信。
send语句从一 个goroutine传输一个值到另一个在receive接收表达式的goroutine。
两个操作都使用 <-操作符书写。发送语句中,通道和值分别在 <-的左右两边。在接收表达式中,<-放在通道操作数前面。在接收表达式中,其结果未被使用也是合法的。
ch <- x //发送语句到channel
X = <-ch //赋值语句中的接收表达式
<-ch //接收语句,丢弃结果
(3)关闭 channel
在我们使用完一个 channel 之后,可以调用 close() 方法来关闭一个 channel, 关闭之后的通道,不能够再进行数据的写操作, 但是仍然可以读取之前写入成功的数据(如果没有数据了,将返回零值)。
close(ch) //关闭channel
- 无缓冲的 channel 的发送操作将导致发送者的 goroutine 阻塞,直到在另一个 goroutine 上对其进行接收操作。如果先发生的是接收操作,那么接收者将被阻塞,直到在另一个 goroutine 上对其进行发送操作。
- 带缓存的 channel 可以缓存多个数据,因此不会立即阻塞,只有当缓存满了之后,发送者才可能会被阻塞,并且只有到缓存为空时,接收者才可能被阻塞
网友评论