并发编程,简单理解就GO了

[文章作者:磨延城 转载请注明原文出处: https://mo2g.com/view/140/ ]

每学习一门新的语言,都需要一段时间去适应他的语法、特性.或许一开始有点陌生,但是万变不离其宗,编程语言只是一种工具而已.所以写代码的时候,都是根据开发者的思路去编写代码,不同的编程语言,写出来的代码不一样,但思路都是相同的,这里的思路就是算法.

之所以学习GO语言,并不是PHP语言不好,只能说是个人兴趣爱好。每一门编程语言,都有合适他的使用场景。就目前看来,GO很适合并发编程。

什么是并发?下边举个简单的例子让大家感受一下。

校运会举行另类400米赛跑,参加比赛的班级以如下形式进行比赛:

A班:派出一名体育生,以52秒的成绩跑完全程,平均每100米需要13秒。

B班:派出4名男同学,以接力的形式跑完全程,平均每100米需要14秒,总耗时56秒。

C班:派出4名女同学,按接力的站位,同时起跑,平均每100米需要20秒,总耗时20秒。

从这个例子,应该很容易看出,即便是那些卡哇依的女同学,在并发编程的领导下,依然可以参加比赛。

每学习一门新的语言,都需要一段时间去适应他的语法、特性。或许一开始有点陌生,但是万变不离其宗,编程语言只是一种工具而已。所以写代码的时候,都是根据开发者的思路去编写代码,不同的编程语言,写出来的代码不一样,但思路都是相同的,这里的思路就是算法。

举个简单的例子,左手拿了2个气球,右手有3个气球,一共有几个气球?

2 + 3 = 5

是的,这是一道很简单的小学数学题,把他转化成运算公式,如下:

x + y = z

这就是算法,只不过不同编程语言,表达方式不一样而已。

所以为了快速学习GO语言的并发编程,我就尝试用GO语言来求解简单的数学题:

把1到max之间能被2或3或5或6整除的整数相加,求和。

我的思路就是:

1)把1到max的整数,分别除以 2、3、5、6,累计没有余数的整数

如果max为10,那就得到 2+3+4+5+6+8+9 = 37,GO语言代码如下:

package main

import (
	"fmt"//导入打印功能依赖的包
)

func main() {
	x := 0//初始化变量x为int
	x = _sum(1, 10)
	fmt.Println(x)//打印x的值
}

func _sum(start int, end int) int {
	sum := 0
	for i := start; i < end; i++ {
		if i%2 == 0 || i%3 == 0 || i%5 == 0 || i%6 == 0 {
			sum += i//累计满足条件的整数
		}
	}
	return sum
}

如果N值很大呢?思路也是一样的,只不过,我们可以在原有的基础上优化一下:

1)假设max为100,把1到100分成2份,比如 1~49,50~100

2)让A把1到50的整数,都分别除以 2、3、5、6,累计没有余数的整数

3)让B把51到100的整数,都分别除以 2、3、5、6,累计没有余数的整数

4)最后把A、B得出的数值相加一次

2)3)步骤就用到了GO语言的并发特性。

我们试着把2)3)步骤完善一下,只要给出一个n值,就能把1~max分成n等份,分配给n个人来运算。这个思路是不是有点意思?

因为使用了并发的特性,所以得重构代码:

package main

import (
	"fmt"
	"runtime"
)

func main() {
	runtime.GOMAXPROCS(2)//设定GO程序能用几个CPU进行运算
	x := 0
	x = go_sum(2, 10)
	fmt.Println(x)
}
/*
num:把1~max分为几份
max:最大值
例如:go_sum(3,10)
把1~11分为3份,多出来的10就单独一份:123 456 789 10,num就变为了4份
*/
func go_sum(num int, max int) int {
	sum, start, end := 0, 0, 0//初始化多个变量
	y := max / num//因为max num都为int,所以运算会把小数点部分舍弃

	if max%num != 0 {
		num += 1
	}
	totle := make(chan int, num)//GO并发需要

	for i := 0; i < num; i++ {
	    //把1~max分成n等份,分配给n个人来运算
		start = y * i
		end = y * (i + 1)

		if start == 0 {
			start = 1
		}

		if end >= max {
			end = max
		}
		go chan_sum(start, end, totle)//分配运算任务
	}
	for i := 0; i < num; i++ {
		sum += <-totle//累计运算结果
	}
	return sum
}
func chan_sum(start int, end int, totle chan int) {
	sum := 0
	for i := start; i < end; i++ {
		if i%2 == 0 || i%3 == 0 || i%5 == 0 || i%6 == 0 {
			sum += i
		}
	}
	totle <- sum
}

上边的代码,我们用到了GO语言的并发特性,先不要去看那些“专业”的名词解释,简单理解就GO了

1)totle := make(chan int, num) 创建能同时存储num个整数的空间totle

2)go chan_sum(start, end, totle) 使用for循环分配并发运算任务,并把结果存入totle,go关键字就是并发的开关

3)totle <- sum 把计算结果存入空间

4)sum += <-totle 使用for循环把空间里的数值读出来,并累计

GO默认使用一个CPU核心运行,runtime.GOMAXPROCS(n)可以设定使用的CPU个数。最好不要超过runtime.GOMAXPROCS(runtime.NumCPU()),CPU之间频繁的上下文切换会浪费时间。上面的代码,我设定了使用2个cpu。

大家肯定想知道这两种方法的效率对比,我给大家一个数据,1~10000000:

第一种方法耗时:24.003ms

第二种方法耗时:16.5021ms

最后就把耗时统计的代码贴出来,有兴趣可以自己测试一下。完整GO代码地址Github

package main

import (
	"fmt"
	"runtime"
	"time"
)
func main() {
	runtime.GOMAXPROCS(3)
	x := 0
	time1 := time.Now()
	x = _sum(1, 10000000)
	time2 := time.Now()
	x = go_sum(2, 10000000)
	time3 := time.Now()
	fmt.Println(x)
	fmt.Println(time2.Sub(time1))
	fmt.Println(time3.Sub(time2))
}


    评论:

    1. 暂无评论...
    2. 我来说两句:

        切换  

      磨途歌检测发现,您当前使用的浏览器版本过低,要想使用画板模式,请先更新浏览器

        切换  

      磨途歌随机验证码