goroutine 加 channel 代替递归调用,突破递归调用的层级限制
package main import (
"fmt"
"github.com/davecgh/go-spew/spew"
"github.com/BurntSushi/toml"
"errors"
"sync"
) type kvData struct {
Prefix string // 前缀
Data map[string]interface{}
} // 递归将所有的层级遍历出来
func unmarshal(m kvData, work chan kvData, result chan []map[string]interface{}, wg *sync.WaitGroup) (error) {
defer wg.Done()
var r []map[string]interface{}
for k, v := range m.Data {
switch v.(type) {
case string, int64, float64, bool:
r = append(r, map[string]interface{}{"k":m.Prefix + k, "v":v})
case map[string]interface{}:
wg.Add(2)
work <- kvData{Prefix:m.Prefix + k + ".", Data : v.(map[string]interface{})}
default:
return errors.New("目前只能识别string、int、float、bool类型的配置")
}
} result <- r return nil
} func main() {
text := `
myip = "1.1.4.51"
type = "red"
[server]
[server.http]
addr="1.11.7.1:5"
[server.grpc]
addr="1.1.1.1:5"` var obj map[string]interface{} if e := toml.Unmarshal([]byte(text), &obj); e != nil {
spew.Dump(e)
} var work = make(chan kvData, 30)
var result = make(chan []map[string]interface{}, 30)
var wg sync.WaitGroup var r []map[string]interface{} wg.Add(2)
if e := unmarshal(kvData{Prefix:"", Data:obj}, work, result, &wg); e != nil {
fmt.Println(e)
return
} var end = make(chan int)
go func() {
for {
select {
case newWork := <-work:
fmt.Println("w")
spew.Dump(newWork)
go unmarshal(newWork, work, result, &wg)
case newResult := <-result:
wg.Done()
fmt.Println("r")
spew.Dump(newResult)
if len(newResult) != 0 {
r = append(r, newResult...)
}
case <-end:
spew.Dump(r)
return
}
}
}()
wg.Wait()
end<-1 fmt.Println("--all----\n")
for _, v := range r {
fmt.Println(" k => ", v["k"])
fmt.Println(" v => ", v["v"])
} return
}
1、创建两个channel(work,result)分别用来存放任务、返回结果。
2、创建一个结构体 kvData 来存放任务以及任务执行的环境依赖。
3、创建sync.WaitGroup 来等待所有的 goroutine 执行完成。
限制递归层级的原因就是递归的栈的释放是从最后一层倒退着向上释放的,其实限制递归层级的条件就是栈的容量。
goroutine 加 channel 在没层级的过程中都会将结果放回到结果的channel(result)、如果需要进一步分析就将需要进一步分析的任务放到 channel(work)中
一方面是并发在执行、一方面是不会造成栈的累积,因此不存在层级的限制。
注:
1、递归在数量比较少的时候速度和内存的占用量是比较好的(没有做具体的实验所有没有具体的数据)
2、goroutine 启动的时候大概需要占用4K的内容,针对于这样的递归来说还是比较大的一个开销
3、goroutine 应该做一个pool,然后反复使用
goroutine 加 channel 代替递归调用,突破递归调用的层级限制的更多相关文章
- php之递归调用,递归创建目录
/* 递归自身调用自身,每次调用把问题简化,直到问题解决 即:把大的任务拆成相同性质的多个小任务完成 */ /* function recsum($n){ if($n>1){ return $n ...
- Python基础_函数闭包、调用、递归
这节的主要内容是函数的几个用法闭包,调用.递归. 一.函数闭包 对闭包更好的理解请看:https://www.cnblogs.com/Lin-Yi/p/7305364.html 我们来看一个简单的例子 ...
- 为什么你学不会递归?告别递归,谈谈我的一些经验 关于集合中一些常考的知识点总结 .net辗转java系列(一)视野 彻底理解cookie,session,token
为什么你学不会递归?告别递归,谈谈我的一些经验 可能很多人在大一的时候,就已经接触了递归了,不过,我敢保证很多人初学者刚开始接触递归的时候,是一脸懵逼的,我当初也是,给我的感觉就是,递归太神奇了! ...
- [转帖]go 的goroutine 以及 channel 的简介.
进程,线程的概念在操作系统的书上已经有详细的介绍.进程是内存资源管理和cpu调度的执行单元.为了有效利用多核处理器的优势,将进程进一步细分,允许一个进程里存在多个线程,这多个线程还是共享同一片内存空间 ...
- Go--关于 goroutine、channel
Go--关于 goroutine.channel goroutine 协程是一种轻量化的线程,由Go编译器进行优化. Go协程具有以下特点: 有独立的栈空间 共享程序堆中的空间 调度由用户控制 如果主 ...
- 求字符串长度之递归与非递归的C语言实现
在上一篇中介绍了字符串拷贝的递归与非递归的实现,这里就不在赘述递归原理. 递归求字符串长度_strlen: 1 int _strlen(const char *src) 2 { 3 if( src = ...
- TODO:Go语言goroutine和channel使用
TODO:Go语言goroutine和channel使用 goroutine是Go语言中的轻量级线程实现,由Go语言运行时(runtime)管理.使用的时候在函数前面加"go"这个 ...
- Go基础--goroutine和channel
goroutine 在go语言中,每一个并发的执行单元叫做一个goroutine 这里说到并发,所以先解释一下并发和并行的概念: 并发:逻辑上具备同时处理多个任务的能力 并行:物理上在同一时刻执行多个 ...
- 记住经典的斐波拉契递归和阶乘递归转换为while规律
记住经典的斐波拉契递归和阶乘递归转换为while规律.它为实现更复杂转换提供了启发性思路. # 斐波拉契--树形递归 def fab(n): if n<3: return n return fa ...
随机推荐
- 通过PHP前端后台交互/通过ajax前端后台交互/php基础传输数据应用/简单的留言版/简单的注册账户/简单的登录页/
前 言 PHP 通过上一篇博客,注册账号与登录页面--前后台数据交互 跳转转到index主页,接下来进入主页留言板功能,通过ajax向后台传输数据,同时发表留言. 具体的内容分析如下 ...
- Java IO学习笔记四
内存操作流 之前的所有的流操作都是针对文件的,但是有时候只是想要实现数据间转换,此时如果我们想要创建一个文件然后再删除文件,那样显得有点麻烦,因此此时的内存操作流就显得很适合这类的操作,因为它只是在内 ...
- Laravel踩坑笔记——illuminate/html被抛弃
起因 在使用如下代码的时候发生报错 {!! Form::open() !!} 错误信息 [Symfony\Component\Debug\Exception\FatalErrorException] ...
- Volley源码分析一
Volley源码分析 虽然在2017年,volley已经是一个逐渐被淘汰的框架,但其代码短小精悍,网络架构设计巧妙,还是有很多值得学习的地方. 第一篇文章,分析了请求队列的代码,请求队列也是我们使用V ...
- EF架构~Migration数据迁移的执行顺序
回到目录 对于单个分支项目来说,只要你生成一个migration的版本,就会有一个时间戳文件的对应,而在update-database时,会从最小的时间开始,一直执行到当前版本的migration,而 ...
- jquery 检测某元素是否含有某属性
检测某元素是否含有某属性 if(typeof($("#aid").attr("rel"))=="undefined")
- python 导入informixdb模块
最近碰到Linux平台使用python连接informixdb数据库的问题.整理如下: 1.安装 informixdb 下载InformixDB-2.5.tar.gz 解压之后,在README文档下看 ...
- 大数据 Hadoop,Spark和Storm
大数据(Big Data) 大数据,官方定义是指那些数据量特别大.数据类别特别复杂的数据集,这种数据集无法用传统的数据库进行存储,管理和处理.大数据的主要特点为数据量大(Volume),数据类别复 ...
- 7.modifier插件的自定义和使用
1.在plugins下面创建一个文件 modifier.changeDate.php 编写: <?php function smarty_modifier_changeDate($utime,$ ...
- Binder的工作原理浅析
在Android开发中,Binder主要用于Service中,包括AIDL和Messenger,其中Messenger的底层实现就是AIDL,所以我们这里通过AIDL来分析一下Binder的工作机制. ...