并发基础
GO语言处理并发非常简单,使用关键词“go”即可。
创建main.go文件,内容如下:
package main
func main() {
go print(1)
}
这是一个非常简单的并发例子。运行 go run main.go,你会发现什么也不会输出。为什么?因为没等print执行,主程就退了。
我们把代码修改下:
package main
import "time"
func main() {
go print(1)
time.Sleep(time.Second)
}
这里停了一秒,使得print有足够的时间输出后结束。假设print执行超过一秒钟,或者我们压根不知道print要执行多少时间,这是后我们就要要到“channel”啦。
package main
import (
"fmt"
"time"
)
func main() {
done := make(chan bool)
go Print(1, done)
for {
<-done
break
}
}
func Print(args interface{}, done chan bool) {
time.Sleep(time.Second * 3)
fmt.Println(args)
done <- true
}
无论我们怎么调整sleep的参数,程序都会等待print输出后才结束。到这里我们想如果Print 3秒钟后还没执行我们就要退出了,这里就可以用到channel的一个超时处理方式:
package main
import (
"fmt"
"time"
)
func main() {
done := make(chan bool)
isTimeOut := make(chan bool)
go Print(1, done)
go func(){
time.Sleep(time.Second*3)
isTimeOut <- true
}()
for {
select {
case <-done:
return
case <-isTimeOut:
println("timeout.")
return
}
}
}
func Print(args interface{}, done chan bool) {
time.Sleep(time.Second * 5)
fmt.Println(args)
done <- true
}
这里go run main.go输出"timeout."。我们可以设置缓冲,如:
result := make(chan int,10)
result一开始会存入10个元素,阻塞直到result的元素被取出且小于10后才能再往result写入数据。
并发应用
使用go并发我们可以进行任务分发:
package main
import (
"fmt"
)
func main() {
result := make(chan int)
rlt := 0
go Sum(1, 50, result)
go Sum(51, 100, result)
//把两次写入到result的值取出来求和
for i := 1; i <= 2; i++ {
rlt += <-result
}
fmt.Println(rlt)
}
func Sum(start, end int, result chan int) {
sum := 0
for i := start; i <= end; i++ {
sum += i
}
result <- sum
}
这里计算1+2+3+..+100的和,并发两个进程处理。当然这里为了演示不考虑算法问题了。
使用GO并发构建工作池,工作池就是能进行特定功能处理的“工人”的集合。这里的“工人”可以理解为我们封装好的函数比如上面sum。看下简单的一个示例:
package main
import (
"fmt"
)
func main() {
start, end := make(chan int), make(chan int)
//启动有五个工人的工作池
for i := 0; i <= 4; i++ {
go Sum(i, start, end)
}
//发送任务给工作池中工人处理
for i := 1; i <= 5; i++ {
start <- i * 5
end <- i * 10
}
}
func Sum(worker int, start, end chan int) {
sum := 0
s := <-start
e := <-end
for i := s; i <= e; i++ {
sum += i
}
fmt.Println("worker ", worker, " -> ", sum)
}
每次go run main.go输出的顺序,结果可能都不一样:
worker 4 -> 45
worker 2 -> 360
worker 1 -> 165
worker 3 -> 630
worker 0 -> 975
worker 4 -> 45
worker 1 -> 165
worker 3 -> 360
worker 0 -> 630
worker 2 -> 975
可以看出,只要哪个“工人”通道中获取到需要计算的值,谁就先处理。