在循环中并发执行goroutine

在循环中并发执行goroutine

看下面的例子,

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func sum(slice []int) (result int) {
	ch := make(chan int)
	fmt.Println(cap(slice))
	fmt.Println(len(slice))

	var num int = 20

	var count int = len(slice) / num

	for i := 0; i < num; i++ {
		from := i * count
		end := count * (i + 1)

		subSlice := slice[from:end]

		// 通过 goroutine 并发的计算
		// 相当于启动了 20 个goroutine
		go func(arr []int) {
			var base int = 0
			for _, v := range arr {
				base += v
			}
			ch <- base
		}(subSlice)
	}

	// Wait for goroutines to complete.
	for i := 0; i < num; i++ {
		x := <-ch
		result += x
	}

	return

}

func sumOther(slice []int) (result int) {
	// for range 遍历切片
	for _, i := range slice {
		result += i
	}
	return
}

func main() {

	r := rand.New(rand.NewSource(time.Now().UnixNano()))

	slice := make([]int, 10000) //定义容量为10000的切片

	for i := 0; i < 10000; i++ {
		slice[i] = r.Intn(100)
	}

	s1 := time.Now().UnixNano()
	res := sum(slice)
	s2 := time.Now().UnixNano()
	fmt.Printf("time=%d,res=%d\n", s2-s1, res)

	s3 := time.Now().UnixNano()
	res2 := sumOther(slice)
	s4 := time.Now().UnixNano()
	fmt.Printf("time=%d,res=%d\n", s4-s3, res2)

	fmt.Println(".........")

}

运行结果:

10000
10000
time=108438,res=497836
time=6394,res=497836

上边的demo中使用随机数构造了一个容量和大小都是10000的切片,求切片中所有整数的和。这里把切片分成20份,使用一个单独的goroutine执行每份的求和,最后汇总结果。注意这里使用的channel不带缓冲,不带缓冲的channel工作过程如下: 一个基于无缓存Channels的发送操作将导致发送者goroutine阻塞,直到另一个goroutine在相同的Channels上执行接收操作,当发送的值通过Channels成功传输之后,两个goroutine可以继续执行后面的语句。反之,如果接收操作先发生,那么接收者goroutine也将阻塞,直到有另一个goroutine在相同的Channels上执行发送操作。基于无缓存Channels的发送和接收操作将导致两个goroutine做一次同步操作。因为这个原因,无缓存Channels有时候也被称为同步Channels。也就是说写入一次channel,必须要读一次channel。如果读取channnel在写入channel之前发生,那么发生阻塞,直到有数据写入channel。

所以这里使用无缓存的channel会带来一些同步操作,将这个demo改成使用带有缓存的channel看看效果如何,

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func sum(slice []int) (result int) {
	ch := make(chan int)

	var num int = 20

	var count int = len(slice) / num

	for i := 0; i < num; i++ {
		from := i * count
		end := count * (i + 1)

		subSlice := slice[from:end]

		// 通过 goroutine 并发的计算
		// 相当于启动了 20 个goroutine
		go func(arr []int) {
			var base int = 0
			for _, v := range arr {
				base += v
			}
			// 一个基于无缓存Channels的发送操作将导致发送者goroutine阻塞,直到另一个goroutine在相同的Channels上执行接收操作
			ch <- base
		}(subSlice)
	}

	// Wait for goroutines to complete.
	for i := 0; i < num; i++ {
		//channel是无缓冲的,接收方会一直阻塞直到有数据到来,发送方会一直阻塞直到接收方将数据取出。
		x := <-ch
		result += x
	}

	return

}

func sumWithCacheChannel(slice []int) (result int) {

	var num int = 20
	ch := make(chan int, num)

	var count int = len(slice) / num

	for i := 0; i < num; i++ {
		from := i * count
		end := count * (i + 1)

		subSlice := slice[from:end]

		// 通过 goroutine 并发的计算
		// 相当于启动了 20 个goroutine
		go func(arr []int) {
			var base int = 0
			for _, v := range arr {
				base += v
			}

			//使用了带缓冲的channel ,发送数据不会阻塞
			ch <- base
		}(subSlice)
	}

	// 预先知道会有 20 个消息发送到channel中,所以这里读取20次,会保证读取到20个消息(没有消息时会阻塞)
	for i := 0; i < num; i++ {
		x := <-ch
		result += x
	}

	return
}

func sumOther(slice []int) (result int) {
	// for range 遍历切片
	for _, i := range slice {
		result += i
	}
	return
}

func main() {

	r := rand.New(rand.NewSource(time.Now().UnixNano()))

	slice := make([]int, 10000) //定义容量为10000的切片

	for i := 0; i < 10000; i++ {
		slice[i] = r.Intn(100)
	}

	s1 := time.Now().UnixNano()
	res := sum(slice)
	s2 := time.Now().UnixNano()
	fmt.Printf("time=%d,res=%d\n", s2-s1, res)

	s3 := time.Now().UnixNano()
	res2 := sumWithCacheChannel(slice)
	s4 := time.Now().UnixNano()
	fmt.Printf("time=%d,res=%d\n", s4-s3, res2)

	s5 := time.Now().UnixNano()
	res3 := sumOther(slice)
	s6 := time.Now().UnixNano()
	fmt.Printf("time=%d,res=%d\n", s6-s5, res3)

}

运行结果,

time=78906,res=491743
time=48826,res=491743
time=8240,res=491743

可以看到使用了带缓存的channel比没有使用带缓存的channel是有一点优势的,就是因为带缓存的channel发送消息不会阻塞,如果channel中有消息接收一端也不会阻塞。但是看来在这里使用goroutine也是多余的。

========END========

转载于:https://my.oschina.net/xinxingegeya/blog/716060

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值