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 02 Golang
Go Gin实现文件上传下载的示例代码
Apr 02 Golang
Go语言-为什么返回值为接口类型,却返回结构体
Apr 24 Golang
彻底理解golang中什么是nil
Apr 29 Golang
golang slice元素去重操作
Apr 30 Golang
Go标准容器之Ring的使用说明
May 05 Golang
Golang之sync.Pool使用详解
May 06 Golang
Golang实现AES对称加密的过程详解
May 20 Golang
Golang并发操作中常见的读写锁详析
Aug 30 Golang
深入理解go slice结构
Sep 15 Golang
golang实现一个简单的websocket聊天室功能
Oct 05 Golang
Golang 结构体数据集合
Apr 22 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
解析WordPress中函数钩子hook的作用及基本用法
2015/12/22 PHP
Django 标签筛选的实现代码(一对多、多对多)
2018/09/05 PHP
[原创]静态页面也可以实现预览 列表不同的显示方式
2006/10/14 Javascript
javascript函数中的arguments参数
2010/08/01 Javascript
JS实现让网页背景图片斜向移动的方法
2015/02/25 Javascript
javascript为按钮注册回车事件(设置默认按钮)的方法
2015/05/09 Javascript
jQuery的deferred对象使用详解
2016/09/25 Javascript
es6的数字处理的方法(5个)
2017/03/16 Javascript
Node.js 使用命令行工具检查更新
2017/06/08 Javascript
深入理解jquery的$.extend()、$.fn和$.fn.extend()
2017/07/08 jQuery
Vue.js 点击按钮显示/隐藏内容的实例代码
2018/02/08 Javascript
对vue里函数的调用顺序介绍
2018/03/17 Javascript
Node.js的进程管理的深入理解
2019/01/09 Javascript
JS隐藏号码中间4位代码实例
2019/04/09 Javascript
基于小程序请求接口wx.request封装的类axios请求
2020/07/02 Javascript
Python正则表达式的使用范例详解
2014/08/08 Python
Python实现KNN邻近算法
2021/01/28 Python
python实现学生管理系统
2018/01/11 Python
Python Requests模拟登录实现图书馆座位自动预约
2018/04/27 Python
python 通过类中一个方法获取另一个方法变量的实例
2019/01/22 Python
Python面向对象程序设计类的多态用法详解
2019/04/12 Python
Pytorch 使用opnecv读入图像由HWC转为BCHW格式方式
2020/06/02 Python
Python中的面向接口编程示例详解
2021/01/17 Python
html5使用canvas画三角形
2014/12/15 HTML / CSS
NEW LOOK官网:英国时装零售巨头之一,快时尚品牌
2017/01/11 全球购物
世界上最伟大的马产品:Equiderma
2020/01/07 全球购物
意大利和国际奢侈品牌购物网站:Suitnegozi.com
2021/01/15 全球购物
广州某公司软件工程师面试题
2014/12/22 面试题
中国梦的演讲稿
2014/01/08 职场文书
《蚕姑娘》教学反思
2014/04/15 职场文书
水污染治理工程专业自荐信
2014/06/21 职场文书
销售内勤岗位职责范本
2015/04/13 职场文书
电信营业员岗位职责
2015/04/14 职场文书
社会主义核心价值观主题教育活动总结
2015/05/07 职场文书
Python图像处理之图像拼接
2021/04/28 Python
python状态机transitions库详解
2021/06/02 Python