golang-goroutine与channel:高效的channel

原创 2016年08月29日 17:33:54
        golang有两个非常大的特性,那就是goruntime与channel,这两个特性直接将开发人员从并发和线程同步中解放了出来,使高并发和线程同步之间代码的编写变得异常简单,并且占用资源少,同步传输效率高。

        资源占用方面,goroutine 会从4096字节的初始栈内存占用开始按需增长或缩减内存占用。同步传输效率方面,我曾经在松本行弘的《代码的未来》一书上看到一个简洁的例子(书上的代码中行末带有分号,目前golang中已经取消了源代码中行末的分号,由编译器代为添加,一行代码包含多个语句则需要分号分隔)。下列代码在原代码基础上做了适当调整。

package main

import (
	"fmt"
	"time"
)

func chanFlow(left, right chan int, bufferLen int) {
	if bufferLen <= 0 {
		left <- 1 + <-right
	} else {
		for i := 0; i < bufferLen; i++ {
			left <- 1 + <-right
		}
	}
}

func main() {
	nruntime := 100000
	lastChan := make(chan int)

	var left chan int = nil
	right := lastChan

	begin := time.Now()
	fmt.Println("begin at:", begin)
	for i := 0; i < nruntime; i++ {
		left, right = right, make(chan int)
		go chanFlow(left, right, 0)
	}

	right <- 0
	result := <-lastChan

	end := time.Now()
	fmt.Println("end   at:", end, time.Since(begin))
	fmt.Println(result)
}

      程序创建了10w零1个无缓冲channel, 10w个goruntime, 数据在goruntime中从第一个channel流向最后一个channel,每流入一次数值加一。代码在我的笔记本(2.7 GHz Intel Core i5, 8 GB 1867 MHz DDR3)运行结果如下:
begin at: 2016-08-28 14:42:04.972728029 +0800 CST
end   at: 2016-08-28 14:42:05.454288408 +0800 CST 481.560725ms
耗时不到半秒。

        上面的例子中使用的是无缓冲的channel,把代码修改为带1000个单位缓冲的channel再试试看,代码如下:

func chanFlow(left, right chan int, bufferLen int) {...}
func main() {
	nruntime := 100000
	chanBuffer := 1000
	result := make([]int, 0, 100)

	lastChan := make(chan int, chanBuffer)

	var left chan int = nil
	right := lastChan

	begin := time.Now()
	fmt.Println("begin at:", begin)
	for i := 0; i < nruntime; i++ {
		left, right = right, make(chan int, chanBuffer)
		go chanFlow(left, right, chanBuffer)
	}

	for i := 0; i < chanBuffer; i++ {
		right <- 0
	}

	for i := 0; i < chanBuffer; i++ {
		result = append(result, <-lastChan)
	}

	end := time.Now()
	fmt.Println("end   at:", end, time.Since(begin))
	fmt.Println(result)
}

运行结果如下:
begin at: 2016-08-28 14:54:09.352472708 +0800 CST
end   at: 2016-08-28 14:54:14.155240335 +0800 CST 4.802767822s
不到5秒的时间,1000个数据在10w个goruntime中穿过了10w零1个channel。

      而在实际生产中,更多的需要传递的数据是字符串,那么现在把代码再修改一下试试,代码如下:

package main

import (
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"io"
	"time"
)

func chanFlow(left, right chan string, bufferLen int) {
	if bufferLen <= 0 {
		left <- <-right
	} else {
		for i := 0; i < bufferLen; i++ {
			left <- <-right
		}
	}
}

func genString() string {
	b := make([]byte, 32)
	if _, err := io.ReadFull(rand.Reader, b); err != nil {
		return ""
	} else {
		return base64.URLEncoding.EncodeToString(b)
	}
}

func main() {

	nruntime := 100000
	chanBuffer := 1000
	result := make([]string, 0, 100)

	lastChan := make(chan string, chanBuffer)
	dataForChan := make([]string, 0, chanBuffer)

	for i := 0; i < chanBuffer; i++ {
		dataForChan = append(dataForChan, genString())
	}

	var left chan string = nil
	right := lastChan

	begin := time.Now()
	fmt.Println("begin at:", begin)
	for i := 0; i < nruntime; i++ {

		left, right = right, make(chan string, chanBuffer)
		go chanFlow(left, right, chanBuffer)
	}
	for i := 0; i < chanBuffer; i++ {
		right <- dataForChan[i]
	}

	for i := 0; i < chanBuffer; i++ {
		result = append(result, <-lastChan)
	}

	end := time.Now()
	fmt.Println("end   at:", end, time.Since(begin))
	fmt.Println(result)
}

运行结果如下:

begin at: 2016-08-28 15:06:25.349599328 +0800 CST
end   at: 2016-08-28 15:06:31.288183546 +0800 CST 5.938584364s
不到6秒的时间,1000个44字节的随机字符串在10w个goruntime中穿过了10w零1个channel。而1w个44字节的随机字符串在1w个goruntime中穿过了1w零1个channel耗时约为5秒。

        以上可以看出,golang中数据在goruntime中通过channel同步的效率非常高。
版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

golang的异步API总结

首先是剧透。这篇文章所讲的东西,其实就是golang和erlang里的并行精髓。文中的问题在golang里可以这样解决: ch := make(chan int); go fun(ch chan ...

GoLang之协程、channel、select、同步锁

GoLang之协程   目前,WebServer几种主流的并发模型: 多线程,每个线程一次处理一个请求,在当前请求处理完成之前不会接收其它请求;但在高并发环境下,多线程的开销比较大;...

Golang协程与通道整理

协程goroutine      不由OS调度,而是用户层自行释放CPU,从而在执行体之间切换。Go在底层进行协助实现      涉及系统调用的地方由Go标准库协助释放CPU      总之,不通...

线程池的原理及实现

1、线程池简介:     多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。        假设一个服务器完成一项任务所需时间为:T...
  • Hsuxu
  • Hsuxu
  • 2013-05-28 17:50
  • 133037

Golang json解析

对于json解析的一点小坑...       1) 返回字段数 等于 定义struct 数  见代码:package main import ( "encoding/json" "fmt" "...

go语言限制Goroutine数量

package main import ( "fmt" "runtime" "time" ) func main() { runtime.GOMAXPROCS(runtime.NumC...

golang使用之使用channel限制goroutine的数量

golang虽然启动一个goroutine很廉价但并不是可以无限制的使用的. 大多数的是用channel来限制goroutine的数量 写了一个测试DEMO如下: package main im...

golang限制协程数量

虽然golang中协程开销很低,但是在一些情况下还是有必要限制一下协程的开启数,比如爬虫中的下载协程,因为受到带宽限制,开的多了也没有效果。本来想在网上找找有没协程池,类似其它语言线程池这样的东西,可...

golang channel的使用技巧

go的横空出世,让很多人眼前一亮,它的语法以简洁著称,并且它对多核并发的原生支持,让他在云计算和分布式领域展露头脚,它的核心围绕channel和goroutine展开。下面我们来介绍一下channel...
  • D_Guco
  • D_Guco
  • 2016-11-25 21:53
  • 1705

十条有用的 GO 技术

十条有用的 Go 技术 这里是我过去几年中编写的大量 Go 代码的经验总结而来的自己的最佳实践。我相信它们具有弹性的。这里的弹性是指:  某个应用需要适配一个灵活的环境。你不希望每过 3 到 ...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)