Go语言实现并行分段求和计算
这个实例通过循环实现并行的分段求和计算,再把各个子段和加到总和中。
通过这个实例可以了解如何实现循环并行处理,以及有关的编程技巧。
但是这个程序是有问题的,因为可能发生变量访问冲突问题,导致计算结果不正确。这个程序是不稳定的,有时能够计算出不正确的结果,有时能够计算出正确结果。
程序中的变量sum是共享变量,需要使用同步用的互斥锁来保证计算的正确性。正解程序附在本博文的最后。
Go语言程序(不稳定,会出错):
// loopgoroutine project main.go package main import ( "fmt" "runtime" "time" ) const START = 1 const LEN = 100 const STEP = 1000 var sum int64 func main() { fmt.Printf("%d\n", (int64(LEN*STEP) * (int64(LEN*STEP) + 1) / int64(2))) sum = 0 start := START for i := 1; i <= STEP; i++ { go subsum(start, LEN) start += LEN } for runtime.NumGoroutine() > 1 { time.Sleep(100 * time.Millisecond) } fmt.Printf("%d\n", sum) } func subsum(start int, len int) { var ssum int64 ssum = 0 for i := 1; i <= len; i++ { ssum += int64(start) start++ } sum += ssum }
运行结果(可能是以下两种,也可能算出其他的结果):
5000050000 5000050000
5000050000 4713772650
程序说明:
1.这个程序总的功能是计算1到100000(LEN×STEP)的和,计算被分为STEP步(段)进行,通过调用函数subsum()计算每段之和,然后相加
2.函数subsum()计算从start开始长度为len的数列之和
3.语句"fmt.Printf("%d\n", (int64(LEN*STEP) * (int64(LEN*STEP) + 1) / int64(2)))"打印输出一个正确的结果作为参考值
4.为了知道程序当前有几个goroutine在运行,需要使用包"runtime",其中的方法runtime.NumGoroutine()返回正在运行的goroutine的数量,需要注意的是main()本身也是一个goroutine
5.使用包"time"中的方法time.Sleep(),让自身的goroutine休眠,代入参数指定休眠0.1秒,因为需要等待所有其他goroutine都执行完之后程序才能结束
6.使用互斥锁mu来锁住变量sum(参见程序),需要使用包"sync"
Go语言程序(正解):
// loopmutex project main.go package main import ( "fmt" "runtime" "sync" "time" ) const START = 1 const LEN = 100 const STEP = 1000 var ( mu sync.Mutex sum int64 ) func main() { fmt.Printf("%d\n", (int64(LEN*STEP) * (int64(LEN*STEP) + 1) / int64(2))) sum = 0 start := START for i := 1; i <= STEP; i++ { go subsum(start, LEN) start += LEN } for runtime.NumGoroutine() > 1 { time.Sleep(100 * time.Millisecond) } fmt.Printf("%d\n", sum) } func subsum(start int, len int) { var ssum int64 ssum = 0 for i := 1; i <= len; i++ { ssum += int64(start) start++ } mu.Lock() sum += ssum mu.Unlock() }