做过Java开发的同学肯定知道,JDK7加入的Fork/Join是一个非常优秀的设计,到了JDK8,又结合并行流中进行了优化和增强,是一个非常好的工具。

1、Fork/Join是什么

Fork/Join本质上是一种任务分解,即:将一个很大的任务分解成若干个小任务,然后再对小任务进一步分解,直到最小颗粒度,然后并发执行。

这么做的优点很明显,就是可以大幅提升计算性能,缺点嘛,也有一点,那就是资源开销要大一些。

在网上找了一张图,任务分解就是这个意思:

2、Golang中的Fork/Join实现

对于Golang中的Fork/Join的实现,我参考了JDK的源码,利用了Goroutine特性,这样就能充分利用MPG模型,不必自己再处理任务窃取等问题了,用起来还是蛮爽的。

废话不多说,请看代码:

package like_fork_joinimport ("fmt""github.com/oklog/ulid/v2"
)const defaultPageSize = 10type MyForkJoinTask struct {size int
}// NewMyTask 初始化一个任务
func NewMyTask(pageSize int) *MyForkJoinTask {var size = defaultPageSizeif pageSize > size {size = pageSize}return &MyForkJoinTask{size: size,}
}// Do 执行任务时,传入一个切片
func (t *MyForkJoinTask) Do(numbers []int) int {JoinCh := make(chan bool, 1)resultCh := make(chan int, 1)t.do(numbers, JoinCh, resultCh, ulid.Make().String())result := <-resultChreturn result
}func (t *MyForkJoinTask) do(numbers []int, joinCh chan bool, resultCh chan int, id string) {defer func() {joinCh <- trueclose(joinCh)close(resultCh)}()fmt.Printf("id %s numbers %+v\n", id, numbers)// 任务小于最小颗粒度时,直接执行逻辑(此处是求和),不再拆分,否则进行分治if len(numbers) <= t.size {var sum = 0for _, number := range numbers {sum += number}resultCh <- sumfmt.Printf("id %s numbers %+v, result %+v\n", id, numbers, sum)return} else {start := 0end := len(numbers)middle := (start + end) / 2// 左leftJoinCh := make(chan bool, 1)leftResultCh := make(chan int, 1)leftId := ulid.Make().String()go t.do(numbers[start:middle], leftJoinCh, leftResultCh, id+"->left->"+leftId)// 右rightJoinCh := make(chan bool, 1)rightResultCh := make(chan int, 1)rightId := ulid.Make().String()go t.do(numbers[middle:], rightJoinCh, rightResultCh, id+"->right->"+rightId)// 等待左边和右边分治子任务结束var leftDone, rightDone = false, falsefor {select {case _, ok := <-leftJoinCh:if ok {fmt.Printf("left %s join done\n", leftId)leftDone = true}case _, ok := <-rightJoinCh:if ok {fmt.Printf("right %s join done\n", rightId)rightDone = true}}if leftDone && rightDone {break}}// 取结果var (left            = 0right           = 0leftResultDone  = falserightResultDone = false)for {select {case l, ok := <-leftResultCh:if ok {fmt.Printf("id %s numbers %+v, left %s return: %+v\n", id, numbers, leftId, left)left = lleftResultDone = true}case r, ok := <-rightResultCh:if ok {fmt.Printf("id %s numbers %+v, right %s return: %+v\n", id, numbers, rightId, right)right = rrightResultDone = true}}if leftResultDone && rightResultDone {break}}resultCh <- left + rightreturn}
}

代码也不复杂,有注释,大家耐心读一下就明白了。

3、测试验证

我写了一个比较有压力的测试用例代码,请看:

package like_fork_joinimport ("fmt""testing"
)func TestMyTask_Do(t1 *testing.T) {type args struct {numbers []int}const max = 10000var nums = make([]int, 0, max)var want = 0for i := 1; i <= max; i++ {nums = append(nums, i)want += i}tests := []struct {name stringargs argswant int}{{name: fmt.Sprintf("sum(1,%d)", max), args: args{numbers: nums}, want: want},}for _, tt := range tests {t1.Run(tt.name, func(t1 *testing.T) {for i := 0; i <= 100; i += 5 {t := NewMyTask(i)if got := t.Do(tt.args.numbers); got != tt.want {t1.Errorf("Do() = %v, want %v", got, tt.want)}}})}
}

测试成功:

    --- PASS: TestMyTask_Do/sum(1,10000) (1257.79s)
PASS

4、小优化

删除所有fmt包的控制台输出,再跑单元测试结果:

=== RUN   TestMyTask_Do
--- PASS: TestMyTask_Do (60.53s)
=== RUN   TestMyTask_Do/sum(1,10000)--- PASS: TestMyTask_Do/sum(1,10000) (60.53s)
PASS

20万次加法计算,长度为1万的数组的20次计算,60秒搞定,性能巨强,Golang就是棒!

5、后续计划

计划后续再研究研究,看能否把执行任务的逻辑做成泛型和函数闭包,给抽象出来,这样就能单独形成一个通用型的代码包,供外部各种应用程序使用了,不过考虑到goroutine的上下文等问题,估计会让代码比较复杂,眼下这个版本足够简单,也能满足绝大多数场景了。

Golang的Fork/Join实现相关推荐

  1. Java Fork/Join与协程

    一.概览 Fork/Join并行方式是获取良好的并行计算性能的一种最简单同时也是最有效的设计技术.Fork/Join并行算法是我们所熟悉的分治算法的并行版本,典型的用法如下: Result solve ...

  2. java fork join原理_细说Fork/Join框架

    什么是Fork/Join框架? Fork/Join框架是JDK1.7提供的一个用于并行执行任务的框架,是一个把大任务分割成若干小任务,最终汇总每个小任务结果后得到大任务结果的框架.Fork就是把一个大 ...

  3. Oracle官方教程之Fork/Join

    原文链接,译文链接,译者:Zach,校对:郑旭东 fork/join框架是ExecutorService接口的一种具体实现,目的是为了帮助你更好地利用多处理器带来的好处.它是为那些能够被递归地拆解成子 ...

  4. fork/join 全面剖析,你可以不用,但是不能不懂!

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:浮云骑士LIN cnblogs.com/linlinismi ...

  5. java fork_浅谈Java的Fork/Join并发框架

    前几天有写到整合并发结果的文章,于是联想到了Fork/Join.因为在我看来整合并发结果其实就是Fork/Join中的Join步骤.所以今天我就把自己对Fork/Join一些浅显的理解记录下来. 1. ...

  6. fork/join 并行编程

    2019独角兽企业重金招聘Python工程师标准>>> import java.util.concurrent.ForkJoinPool; import java.util.conc ...

  7. join left 大数据_Java并发编程笔记-JDK内置并行执行框架Fork/Join

    Fork/Join由来-分而治之思想 分而治之:对于一个比较复杂的任务,如果可以很自然地将其分解为多个子任务,这些子任务互相独立且与原问题性质相同,递归地处理这些子任务,然后将各个子任务的结果合并得到 ...

  8. JAVA Fork Join Demo 1

    2019独角兽企业重金招聘Python工程师标准>>> package com.famous.thread.util;import java.util.concurrent.Fork ...

  9. 使用Fork/Join框架优化归并排序

    Fork/Join框架是一个非常有意思的并发框架,它非常适合于处理类似归并排序这种将大的问题分解成多个小问题,并将结果进行合并的情况,这次我们就使用Fork/Join框架来优化我们的归并排序.查看更多 ...

最新文章

  1. @EnableAutoConfiguration原理简单分析
  2. C语言中的预处理详解
  3. 七、redis的安装(linux)
  4. INT_MAX和INT_MIN注意事项
  5. Oracle 系统改变号SCN详解
  6. 阿里技术专家:数据一致性检测的应用场景与最佳实践
  7. 干货 | 解读MySQL 8.0新特性:Skip Scan Range
  8. [Android] 输入系统(三):加载按键映射
  9. ajax success function_Django:AJAX(二)
  10. PHP中Cookie和Session的对比
  11. 【转】两个算法题,感觉挺有意思
  12. 如何用Java实现进度条
  13. 移动设备数据丢失恢复办法
  14. Unity背包系统 设计流程
  15. 2011年第36届大连赛区现场赛Board
  16. Excel实现给加单引号,以及加逗号
  17. 怎样使用计算机函数求出等级,巧用Excel函数出练习题
  18. 卡迪夫大数据专业排名_2019QS排名出炉啦!看看卡迪夫大学那些世界前百强的学科...
  19. 学习游戏模型3d角色,出来好找工作吗?
  20. 计算机含金量最高的证书

热门文章

  1. mybatis读写分离
  2. Z01 fstream中ofstream的简单用法
  3. 全基因组重测序基础分析
  4. 计算机的网络配置的方法是什么,怎么配置计算机网络
  5. HR面试经典50题,面试官惊讶了、这是什么操作?
  6. 利用python爬取豆瓣音乐_Python使用Beautiful Soup爬取豆瓣音乐排行榜过程解析
  7. Android 身高体重曲线的实现
  8. 一个suspend的问题分析
  9. 读取哔哩哔哩网站下载的json字幕,并将其内容转换成srt字幕保存
  10. 微信小程序——如何解决本地图片不显示的问题