golang对比其它语言最大的优势就是一个go关键字就能实现并发,工作中经常遇到并发的场景, 具体实现并发的方式有多种,今天就来做个小实验来对比说明串行和并行执行任务中并发的优势,好了直接上代码
package main import ( "fmt" "math/rand" "sync" "time" ) //定义任务数量 var jobsCount int = 10 func main() { timer("serial", serial) timer("parallel", parallel) timer("parallel2", parallel2) } //串行实现任务执行 func serial() { for i := 0; i < jobsCount; i++ { fmt.Printf("hello task %d \n", i) a := int(rand.Float64() * 1000.0) a = 500 //可注释掉该行使用随机数 time.Sleep(time.Duration(a) * time.Millisecond) } fmt.Println("serial task done success ") } //使用waitgroup 来实现等待并发完成 func parallel() { wg := sync.WaitGroup{} for i := 0; i < jobsCount; i++ { wg.Add(1) go func(idx int) { fmt.Printf("hello task %d \n", idx) a := int(rand.Float64() * 1000.0) a = 500 //可注释掉该行使用随机数 time.Sleep(time.Duration(a) * time.Millisecond) wg.Done() }(i) } wg.Wait() fmt.Println("parallel task done success ") } //使用chanel 来实现并发 func parallel2() { ch := make(chan int) //done := make(chan int) for i := 0; i < jobsCount; i++ { go DoTask(i, ch) } for i := 0; i < jobsCount; i++ { fmt.Printf(" task %d done \n", <-ch) } } //我是完成耗时工作的 func DoTask(taskid int, ch chan int) { fmt.Printf("task %d doing !!!!!!!!!!!!!\n", taskid) a := int(rand.Float64() * 1000.0) a = 500 time.Sleep(time.Duration(a) * time.Millisecond) ch <- taskid } //测量函数执行时间 func timer(funcName string, f func()) { t1 := time.Now() f() fmt.Printf("%s fun cost %s \n", funcName, time.Now().Sub(t1)) }
这里假设要完成十个耗时的任务,每个任务需要500ms或者随机时间,分别用串行和两个并行函数来完成;
串行执行的顺序就是每个任务执行完才能执行下个任务,并行执行就是十个任务同时执行不分先后(可以认为同时开始),
使用计时器对每个函数执行时间进行度量,
main 启动后,查看控制台上结果的输出:
hello task 0 hello task 1 hello task 2 hello task 3 hello task 4 hello task 5 hello task 6 hello task 7 hello task 8 hello task 9 serial task done success serial fun cost 5.0042001s hello task 9 hello task 4 hello task 5 hello task 6 hello task 7 hello task 8 hello task 2 hello task 0 hello task 3 hello task 1 parallel task done success parallel fun cost 500.0504ms task 9 doing !!!!!!!!!!!!! task 4 doing !!!!!!!!!!!!! task 3 doing !!!!!!!!!!!!! task 6 doing !!!!!!!!!!!!! task 7 doing !!!!!!!!!!!!! task 8 doing !!!!!!!!!!!!! task 1 doing !!!!!!!!!!!!! task 0 doing !!!!!!!!!!!!! task 2 doing !!!!!!!!!!!!! task 5 doing !!!!!!!!!!!!! task 3 done task 4 done task 1 done task 9 done task 2 done task 0 done task 7 done task 8 done task 6 done task 5 done parallel2 fun cost 500.3297ms Process finished with exit code 0
从控制台上我们可以看到串行执行的话,每个任务500ms ,总任务执行了5s ,
并行执行的话,两个并行并没有顺序,每个任务500ms,总任务也可以认为是500ms,
串行的代码10行,并行的代码分别为 15行和20行 ,相比之下时间上后者是前者的1/10,这就是并行的巨大优势,因此我们要多审视自己的代码,避免偷懒,面对高并发场景下才能实现高效完成任务;
新增循环中使用管道chanel的形式
//使用chan 接收等待任务完成(缺点:增加开销) func paralle3() { //ch := make(chan int, jobsCount) ch := make(chan int) for i := 0; i < jobsCount; i++ { go func(idx int) { fmt.Printf("hello task %d \n", idx) a := int(rand.Float64() * 1000.0) a = 500 //可注释掉该行使用随机数 time.Sleep(time.Duration(a) * time.Millisecond) ch <- idx }(i) } for i := 0; i < jobsCount; i++ { fmt.Printf(" task %d done \n", <-ch) } fmt.Println("paralle3 task done success ") }