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语言操作数据库及其常规操作的示例代码
Apr 21 Golang
golang interface判断为空nil的实现代码
Apr 24 Golang
golang http使用踩过的坑与填坑指南
Apr 27 Golang
golang 如何用反射reflect操作结构体
Apr 28 Golang
Golang 空map和未初始化map的注意事项说明
Apr 29 Golang
Golang中interface{}转为数组的操作
Apr 30 Golang
浅谈golang 中time.After释放的问题
May 05 Golang
完美解决golang go get私有仓库的问题
May 05 Golang
Go语言的协程上下文的几个方法和用法
Apr 11 Golang
Go gRPC进阶教程gRPC转换HTTP
Jun 16 Golang
GoFrame基于性能测试得知grpool使用场景
Jun 21 Golang
Go本地测试解耦任务拆解及沟通详解Go本地测试的思路沟通的重要性总结
Jun 21 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
浅析php中常量,变量的作用域和生存周期
2013/08/10 PHP
PHP进阶学习之垃圾回收机制详解
2019/06/18 PHP
在JavaScript中,为什么要尽可能使用局部变量?
2009/04/06 Javascript
jQuery 使用手册(七)
2009/09/23 Javascript
js 未结束的字符串常量错误解决方法
2010/06/13 Javascript
简单实用jquery版三级联动select示例
2013/07/04 Javascript
js控制表单不能输入空格的小例子
2013/11/20 Javascript
JS将制定内容复制到剪切板示例代码
2014/02/11 Javascript
AngularJS + Node.js + MongoDB开发的基于高德地图位置的通讯录
2015/01/02 Javascript
JavaScript学习笔记之JS事件对象
2015/01/22 Javascript
深入理解JavaScript系列(17):面向对象编程之概论详细介绍
2015/03/04 Javascript
JavaScript实现仿淘宝商品购买数量的增减效果
2016/01/22 Javascript
JavaScript获取服务器端时间的方法
2016/11/29 Javascript
js中数组的常用方法小结
2016/12/30 Javascript
浅谈js的解析顺序 作用域 严格模式
2017/10/23 Javascript
vue.js语法及常用指令
2017/10/29 Javascript
jQuery实现带右侧索引功能的通讯录示例【附源码下载】
2018/04/17 jQuery
总结javascript三元运算符知识点
2018/09/28 Javascript
如何在基于vue-cli的项目自定义打包环境
2018/11/10 Javascript
jQuery实现购物车的总价计算和总价传值功能
2018/11/28 jQuery
jQuery实现左右两个列表框的内容相互移动功能示例
2019/01/27 jQuery
在node环境下parse Smarty模板的使用示例代码
2019/11/15 Javascript
Vue使用富文本编辑器Vue-Quill-Editor(含图片自定义上传服务、清除复制粘贴样式等)
2020/05/15 Javascript
node使用async_hooks模块进行请求追踪
2021/01/28 Javascript
简单解析Django框架中的表单验证
2015/07/17 Python
Python编程中对super函数的正确理解和用法解析
2016/07/02 Python
Python3 循环语句(for、while、break、range等)
2017/11/20 Python
Linux-ubuntu16.04 Python3.5配置OpenCV3.2的方法
2018/04/02 Python
如何基于python测量代码运行时间
2019/12/25 Python
python 在threading中如何处理主进程和子线程的关系
2020/04/25 Python
pycharm 添加解释器的方法步骤
2020/08/31 Python
俄罗斯设计师家具购物网站:The Furnish
2019/12/01 全球购物
物业管理公司实习生自我鉴定
2013/09/19 职场文书
毕业生自我鉴定范文
2013/11/08 职场文书
在职证明范本
2015/06/15 职场文书
Elasticsearch 批量操作
2022/04/19 Python