golang 实现并发求和


Posted in Golang onMay 08, 2021

使用golang并发求和,作为对golang并发的一个练习.

为了验证结果的正确性,要给出最传统的版本:

func sum1(data []int) int {
 s := 0
 l := len(data)
 for i := 0; i < l; i++ {
  s += data[i]
 }
 return s
}

第二种方法

使用N个goroutine, 然后将N个分段的和写入N个channel中:

func sum2(data []int) int {
 s := 0
 l := len(data)
 const N = 5
 seg := l / N
 var chs [N]<-chan int
 for i := 0; i < N; i++ {
  chs[i] = worker(data[i*seg : (i+1)*seg])
 }
 for i := 0; i < N; i++ {
  s += <-chs[i]
 }
 return s
}
func worker(s []int) <-chan int {
 out := make(chan int)
 go func() {
  length := len(s)
  sum := 0
  for i := 0; i < length; i++ {
   sum += s[i]
  }
  out <- sum
 }()
 return out
}

对于一个求和的任务来说,用worker这种“模式”可能 太过麻烦,

看第三种

直接一个函数写出来:

func sum3(data []int) int {
 s := 0
 l := len(data)
 const N = 5
 seg := l / N
 var mu sync.Mutex
 var wg sync.WaitGroup
 wg.Add(N) // 直接加N个
 for i := 0; i < N; i++ {
  go func(ii int) {
   tmpS := data[ii*seg : (ii+1)*seg]
   ll := len(tmpS)
   mu.Lock()
   for i := 0; i < ll; i++ {
    s += tmpS[i]
   }
   mu.Unlock()
   wg.Done() // 一个goroutine运行完
  }(i)
 }
 wg.Wait() // 等N个goroutine都运行完
 return s
}

注意sum3要在读写s的地方加锁,因为s可能被多个goroutine并发读写。

最后一种方法有data race问题

不过运行结果是对的,看一下思路:

var sum4Tmp int
var sum4mu sync.Mutex
// 这个有data race问题,可以用WaitGroup改,只是提供一种思路
func sum4(data []int) int {
 //s := 0
 l := len(data)
 const N = 5
 seg := l / N
 for i := 0; i < N; i++ {
  go subsum4(data[i*seg : (i+1)*seg])
 }
 // 这里是>1,因为要排除main
 // 这种方法不可靠,只是一种思路
 for runtime.NumGoroutine() > 1 {
 }
 // go run -race sum.go会报data race问题
 // main goroutine对它读
 // 别的goroutine会对它写(go subsum4)
 return sum4Tmp
}
func subsum4(s []int) {
 length := len(s)
 sum := 0
 sum4mu.Lock()
 for i := 0; i < length; i++ {
  sum += s[i]
 }
 sum4Tmp = sum4Tmp + sum
 defer sum4mu.Unlock()
}

最后测试如下:

首先创建一个slice, 放1e8(1亿)个整数(范围[0,10))进去,

然后用4种方法进行计算

func calcTime(f func([]int) int, arr []int, tag string) {
 t1 := time.Now().UnixNano()
 s := f(arr)
 t2 := time.Now().UnixNano() - t1
 fmt.Printf("%15s: time: %d, sum: %d\n", tag, t2, s)
}
func main() {
 const MAX = 1e8 // 1亿
 arr := make([]int, MAX)
 for i := 0; i < MAX; i++ {
  arr[i] = rand.Intn(10)
 }
 calcTime(sum1, arr, "for")
 calcTime(sum2, arr, "worker")
 calcTime(sum3, arr, "WaitGroup")
 calcTime(sum4, arr, "NumGoroutine")
}

我的笔记本输出结果:

for: time: 61834200, sum: 450032946

worker: time: 51861100, sum: 450032946

WaitGroup: time: 153628200, sum: 450032946

NumGoroutine: time: 63791300, sum: 450032946

欢迎补充指正!

补充:Golang并发求和(竞争而非分段)

举例

如果要求2个goroutine并发完成1到100的和而不是分段的情况如何解决呢?

解决方案:

var wg sync.WaitGroup
var ch chan int32
var receiveCh chan int32
func add(){
	var sum int32
	sum = 0
	Loop:
	for {
		select {
		case val, ok := <-ch:
			if ok {
				atomic.AddInt32(&sum, val)
			} else {
				break Loop
			}
		}
	}
	receiveCh <- sum
	wg.Done()
}
func main() {
	wg.Add(3)
	ch = make(chan int32)
	receiveCh = make(chan int32, 2)
	go func(){
		for i := 1; i <= 100; i++{
			n := i //避免数据竞争
			ch <- int32(n) 
		}
		close(ch)
		wg.Done()
	}()
	go add()
	go add()
	wg.Wait()
	close(receiveCh)
	var sum int32
	sum = 0
	for res := range receiveCh{
		sum += res
	}
	fmt.Println("sum:",sum)
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持三水点靠木。如有错误或未考虑完全的地方,望不吝赐教。

Golang 相关文章推荐
Go语言使用select{}阻塞main函数介绍
Apr 25 Golang
golang正则之命名分组方式
Apr 25 Golang
golang中切片copy复制和等号复制的区别介绍
Apr 27 Golang
golang在GRPC中设置client的超时时间
Apr 27 Golang
Golang 空map和未初始化map的注意事项说明
Apr 29 Golang
浅谈Golang 嵌套 interface 的赋值问题
Apr 29 Golang
Go使用协程交替打印字符
Apr 29 Golang
go goroutine 怎样进行错误处理
Jul 16 Golang
浅谈GO中的Channel以及死锁的造成
Mar 18 Golang
golang三种设计模式之简单工厂、方法工厂和抽象工厂
Apr 10 Golang
Go语言编译原理之变量捕获
Aug 05 Golang
golang中的并发和并行
May 08 #Golang
关于golang高并发的实现与注意事项说明
May 08 #Golang
基于Golang 高并发问题的解决方案
May 08 #Golang
使用golang编写一个并发工作队列
May 08 #Golang
Go 在 MongoDB 中常用查询与修改的操作
May 07 #Golang
golang 实现时间戳和时间的转化
May 07 #Golang
Golang Gob编码(gob包的使用详解)
May 07 #Golang
You might like
PHP5中使用PDO连接数据库的方法
2010/08/01 PHP
再推荐十款免费的php开发工具
2015/11/09 PHP
php查询操作实现投票功能
2016/05/09 PHP
php+ajax+json 详解及实例代码
2016/12/12 PHP
PHP实现的字符串匹配算法示例【sunday算法】
2017/12/19 PHP
Laravel 类和接口注入相关的代码
2019/10/15 PHP
密码框显示提示文字jquery示例
2013/08/29 Javascript
AngularJs $parse、$eval和$observe、$watch详解
2016/09/21 Javascript
详解基于node的前端项目编译时内存溢出问题
2017/08/01 Javascript
微信小程序 input表单与redio及下拉列表的使用实例
2017/09/20 Javascript
分析javascript原型及原型链
2018/03/18 Javascript
js合并两个数组生成合并后的key:value数组
2018/05/09 Javascript
JavaScript设计模式之建造者模式实例教程
2018/07/02 Javascript
vue首次赋值不触发watch的解决方法
2018/09/11 Javascript
6种JavaScript继承方式及优缺点(小结)
2020/02/06 Javascript
Python计算已经过去多少个周末的方法
2015/07/25 Python
Python解析最简单的验证码
2016/01/07 Python
python cx_Oracle的基础使用方法(连接和增删改查)
2017/11/19 Python
Python基于多线程操作数据库相关问题分析
2018/07/11 Python
Python字符串的一些操作方法总结
2019/06/10 Python
Python多进程编程multiprocessing代码实例
2020/03/12 Python
Django 项目布局方法(值得推荐)
2020/03/22 Python
python利用appium实现手机APP自动化的示例
2021/01/26 Python
LN-CC美国:伦敦时尚生活的缩影
2019/02/19 全球购物
Bibloo匈牙利:女装、男装、童装及鞋子和配饰
2019/04/14 全球购物
英国奢侈品概念店:Base Blu
2019/05/16 全球购物
西班牙鞋子和箱包在线销售网站:zapatos.es
2020/02/17 全球购物
what is the difference between ext2 and ext3
2015/08/25 面试题
一套英文Java笔试题面试题
2016/04/21 面试题
最新销售员个人自荐信
2013/09/21 职场文书
党员创先争优公开承诺书
2014/03/28 职场文书
《李广射虎》教学反思
2014/04/27 职场文书
市场营销专业求职信
2014/06/17 职场文书
太空授课观后感
2015/06/17 职场文书
捐书仪式主持词
2015/07/04 职场文书
初中语文教学研修日志
2015/11/13 职场文书