GO并发
在一个函数调用前加上go关键字,这次调用就会在一个新的goroutine中并发执行。
func Add(x, y int) {
z := x + y
fmt.Println(z)
}
func main() {
for i := 0; i < 10; i++ {
go Add(i, i)
}
}
Go程序从初始化main package并执行main()函数开始,当main()函数返回时,程序退出.且程序并不等待
其他goroutine(非主goroutine)结束。
并发通信
GO语言既以并发编程作为语言的最核心优势,提供了一种通信模型,即以消息机制而非共享内存作为通信方式。
消息机制认为每个并发单元是自包含的、独立的个体,并且都有自己的变量,但在不同并发单元间这些变量不共享。每个并发单元的输入和输出只有一种,那就是消息。
这有点类似于进程的概念,每个进程不会被其他进程打扰,它只做好自己的工作就可以了。不同进程间靠消息来通信,它们不会共享内存。
Go语言提供的消息通信机制被称为channel
“不要通过共享内存来通信,而应该通过通信来共享内存。”
channel
channel是Go语言在语言级别提供的goroutine间的通信方式。channel是类型相关的
chan声明
var chanName chan ElementType
如:
var chint chan int //什么一个int类型的chan
var mch map[string] chan bool //声明一个map,其元素为bool
var arrch [10]chan int //定义一个
实例
func Count(ch chan int) {
ch <- 1
fmt.Println("Counting")
}
func main() {
chs := make([]chan int, 10)
for i := 0; i < 10; i++ {
chs[i] = make(chan int)
go Count(chs[i])
}
for _, ch := range(chs) {
<-ch
}
}
定义channel
直接使用内置的函数make()即可:
ch := make(chan int)
channel基本用法
写入
<span style="white-space:pre"> </span>ch < 1
向channel写入数据通常会导致程序阻塞,直到有其他goroutine从这个channel中读取数据
读出
i := < ch
如果ch没有数据也会导致程序阻塞,直到有数据写入ch
但是,对ch的阻塞,可以利用ch的缓冲机制以及select来灵活出来
缓冲机制
带缓冲的channel适合于需连续传输大量数据的场景,定义一个带缓冲的channel,只需将容量传入make的第二个参数即可
c := make(chan int, 1024) //创建了一个大小为1024的整形chan,写入在缓冲区满前 不会阻塞
channel是可传递的
单向channel
单向channel只能用于发送或者接收数据。
声明
var ch1 chan int // ch1是一个正常的channel,不是单向的
var ch2 chan<- float64// ch2是单向channel,只用于写float64数据
var ch3 <-chan int // ch3是单向channel,只用于读取int数据
初始化
channel可以
在单向channel和双向channel之间进行转换,channel本身就是GO的原生类型,因此可被传递,也可类型转换
ch4 := make(chan int)
ch5 := <-chan int(ch4) // ch5就是一个单向的读取channel
ch6 := chan<- int(ch4) // ch6 是一个单向的写入channel
关闭channel
直接用 close即可
close(ch)
判断channel是否已关闭,可以用多值返回读,如果第二个bool返回值是false则表示ch已经被关闭
x, ok := <-ch
select
用来监控一系列文件句柄发生的IO操作,一旦有其中一个句柄发生IO操作,则被返回
GO在语言级别支持select
代码结构大致如:
select {
<span style="white-space:pre"> </span>case <-chan1:
<span style="white-space:pre"> </span>// 如果chan1成功读到数据,则进行该case处理语句
<span style="white-space:pre"> </span>case chan2 <- 1:
<span style="white-space:pre"> </span>// 如果成功向chan2写入数据,则进行该case处理语句
<span style="white-space:pre"> </span>default:
<span style="white-space:pre"> </span>// 如果上面都没有成功,则进入default处理流程
}
其中每个case必须是一个文件句柄操作
结合for可实现循环检测
ch := make(chan int, 1)
for {
<span style="white-space:pre"> </span>select {
<span style="white-space:pre"> </span>case ch <- 0:
<span style="white-space:pre"> </span>case ch <- 1:
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>i := <-ch
<span style="white-space:pre"> </span>fmt.Println("Value received:", i)
}