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中semaphore(信号量)源码
Apr 03 Golang
go语言-在mac下brew升级golang
Apr 25 Golang
解决golang结构体tag编译错误的问题
May 02 Golang
Golang: 内建容器的用法
May 05 Golang
Go 自定义package包设置与导入操作
May 06 Golang
Go timer如何调度
Jun 09 Golang
golang 实用库gotable的具体使用
Jul 01 Golang
使用GO语言实现Mysql数据库CURD的简单示例
Aug 07 Golang
Golang使用Panic与Recover进行错误捕获
Mar 22 Golang
Golang 对es的操作实例
Apr 20 Golang
实现GO语言对数组切片去重
Apr 20 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把小数转成整数3种方法
2014/06/30 PHP
几个高效,简洁的字符处理函数
2007/04/12 Javascript
jquery live()调用不存在的解决方法
2014/02/26 Javascript
JS中判断JSON数据是否存在某字段的方法
2014/03/07 Javascript
jquery实现效果比较好的table选中行颜色
2014/03/25 Javascript
window.print打印指定div指定网页指定区域的方法
2014/08/04 Javascript
详解JavaScript中Date.UTC()方法的使用
2015/06/12 Javascript
javaScript实现滚动新闻的方法
2015/07/30 Javascript
javascript插件开发的一些感想和心得
2016/02/28 Javascript
ES6中Math对象的部分扩展
2017/02/20 Javascript
js实现百度登录框鼠标拖拽效果
2017/03/07 Javascript
Vue + Webpack + Vue-loader学习教程之功能介绍篇
2017/03/14 Javascript
JQuery实现定时刷新功能代码
2017/05/09 jQuery
如何让你的JS代码更好看易读
2017/12/01 Javascript
在一个页面实现两个zTree联动的方法
2017/12/20 Javascript
angular2组件中定时刷新并清除定时器的实例讲解
2018/08/31 Javascript
详解基于iview-ui的导航栏路径(面包屑)配置
2019/02/22 Javascript
浅谈Vue CLI 3结合Lerna进行UI框架设计
2019/04/14 Javascript
layui复选框的全选与取消实现方法
2019/09/02 Javascript
JavaScript中展开运算符及应用的实例代码
2021/01/14 Javascript
Python素数检测的方法
2015/05/11 Python
Python使用zip合并相邻列表项的方法示例
2018/03/17 Python
设置python3为默认python的方法
2018/10/31 Python
在PyCharm中实现关闭一个死循环程序的方法
2018/11/29 Python
python读取各种文件数据方法解析
2018/12/29 Python
python使用多线程编写tcp客户端程序
2019/09/02 Python
python 解决flask uwsgi 获取不到全局变量的问题
2019/12/22 Python
解决selenium+Headless Chrome实现不弹出浏览器自动化登录的问题
2021/01/09 Python
Jacadi Paris美国官方网站:法国童装品牌
2017/10/15 全球购物
iHerb俄罗斯:维生素、补品和天然产品
2020/07/09 全球购物
毕业生的自我鉴定该怎么写
2013/12/02 职场文书
夜班门卫岗位职责
2013/12/09 职场文书
目标责任书范本
2014/04/16 职场文书
指导教师推荐意见
2015/06/05 职场文书
2016年大学校运会广播稿件
2015/12/21 职场文书
2016年妇联“6﹒26国际禁毒日”宣传活动总结
2016/04/05 职场文书