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缓冲channel和非缓冲channel的区别说明
Apr 25 Golang
解决Golang中ResponseWriter的一个坑
Apr 27 Golang
基于Go Int转string几种方式性能测试
Apr 28 Golang
使用Golang的channel交叉打印两个数组的操作
Apr 29 Golang
go语言中fallthrough的用法说明
May 06 Golang
关于golang高并发的实现与注意事项说明
May 08 Golang
浅谈Golang 切片(slice)扩容机制的原理
Jun 09 Golang
Go语言空白表示符_的实例用法
Jul 04 Golang
go goroutine 怎样进行错误处理
Jul 16 Golang
Go语言的协程上下文的几个方法和用法
Apr 11 Golang
golang定时器
Apr 14 Golang
Go gorilla securecookie库的安装使用详解
Aug 14 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
第1次亲密接触PHP5(1)
2006/10/09 PHP
解析在PHP中使用全局变量的几种方法
2013/06/24 PHP
ThinkPHP3.1新特性之字段合法性检测详解
2014/06/19 PHP
解密ThinkPHP3.1.2版本之独立分组功能应用
2014/06/19 PHP
php广告加载类用法实例
2014/09/23 PHP
PHP简单实现文本计数器的方法
2016/04/28 PHP
PHP扩展框架之Yaf框架的安装与使用
2016/05/18 PHP
Avengerls vs Newbee BO3 第一场2.18
2021/03/10 DOTA
ie 处理 gif动画 的onload 事件的一个 bug
2007/04/12 Javascript
用Juery网页选项卡实现代码
2011/06/13 Javascript
JavaScript在for循环中绑定事件解决事件参数不同的情况
2014/01/20 Javascript
自己封装的javascript事件队列函数版
2014/06/12 Javascript
jQuery设置和移除文本框默认值的方法
2015/03/09 Javascript
nodejs中实现阻塞实例
2015/03/24 NodeJs
BootStrap实现邮件列表的分页和模态框添加邮件的功能
2016/10/13 Javascript
学习vue.js表单控件绑定操作
2016/12/05 Javascript
全新打包工具parcel零配置vue开发脚手架
2018/01/11 Javascript
浅入深出Vue之自动化路由
2019/08/06 Javascript
高性能web服务器框架Tornado简单实现restful接口及开发实例
2014/07/16 Python
对Python3中的print函数以及与python2的对比分析
2018/05/02 Python
很酷的python表白工具 你喜欢我吗
2019/04/11 Python
Python多线程threading模块用法实例分析
2019/05/22 Python
浅析Django中关于session的使用
2019/12/30 Python
Django 再谈一谈json序列化
2020/03/16 Python
Staples英国官方网站:办公用品一站式采购
2017/10/06 全球购物
戛纳奢侈品商店:Jacques Loup法国
2019/11/04 全球购物
大学生简历的个人自我评价
2013/12/04 职场文书
电子商务专业应届生求职信
2014/05/28 职场文书
公司总经理任命书
2014/06/05 职场文书
学雷锋标兵事迹材料
2014/08/18 职场文书
教师四风问题对照检查材料
2014/09/26 职场文书
2014年医德医风工作总结
2014/11/13 职场文书
家装业务员岗位职责
2015/04/03 职场文书
集团财务总监岗位职责
2015/04/03 职场文书
mysql中between的边界,范围说明
2021/06/08 MySQL
JavaScript 事件捕获冒泡与捕获详情
2021/11/11 Javascript