简单看懂GO语言并发

原创
2017/08/03 13:58
阅读数 230

并发基础

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

可以看出,只要哪个“工人”通道中获取到需要计算的值,谁就先处理。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部