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 代替递归调用,突破递归调用的层级限制的更多相关文章

  1. php之递归调用,递归创建目录

    /* 递归自身调用自身,每次调用把问题简化,直到问题解决 即:把大的任务拆成相同性质的多个小任务完成 */ /* function recsum($n){ if($n>1){ return $n ...

  2. Python基础_函数闭包、调用、递归

    这节的主要内容是函数的几个用法闭包,调用.递归. 一.函数闭包 对闭包更好的理解请看:https://www.cnblogs.com/Lin-Yi/p/7305364.html 我们来看一个简单的例子 ...

  3. 为什么你学不会递归?告别递归,谈谈我的一些经验 关于集合中一些常考的知识点总结 .net辗转java系列(一)视野 彻底理解cookie,session,token

    为什么你学不会递归?告别递归,谈谈我的一些经验   可能很多人在大一的时候,就已经接触了递归了,不过,我敢保证很多人初学者刚开始接触递归的时候,是一脸懵逼的,我当初也是,给我的感觉就是,递归太神奇了! ...

  4. [转帖]go 的goroutine 以及 channel 的简介.

    进程,线程的概念在操作系统的书上已经有详细的介绍.进程是内存资源管理和cpu调度的执行单元.为了有效利用多核处理器的优势,将进程进一步细分,允许一个进程里存在多个线程,这多个线程还是共享同一片内存空间 ...

  5. Go--关于 goroutine、channel

    Go--关于 goroutine.channel goroutine 协程是一种轻量化的线程,由Go编译器进行优化. Go协程具有以下特点: 有独立的栈空间 共享程序堆中的空间 调度由用户控制 如果主 ...

  6. 求字符串长度之递归与非递归的C语言实现

    在上一篇中介绍了字符串拷贝的递归与非递归的实现,这里就不在赘述递归原理. 递归求字符串长度_strlen: 1 int _strlen(const char *src) 2 { 3 if( src = ...

  7. TODO:Go语言goroutine和channel使用

    TODO:Go语言goroutine和channel使用 goroutine是Go语言中的轻量级线程实现,由Go语言运行时(runtime)管理.使用的时候在函数前面加"go"这个 ...

  8. Go基础--goroutine和channel

    goroutine 在go语言中,每一个并发的执行单元叫做一个goroutine 这里说到并发,所以先解释一下并发和并行的概念: 并发:逻辑上具备同时处理多个任务的能力 并行:物理上在同一时刻执行多个 ...

  9. 记住经典的斐波拉契递归和阶乘递归转换为while规律

    记住经典的斐波拉契递归和阶乘递归转换为while规律.它为实现更复杂转换提供了启发性思路. # 斐波拉契--树形递归 def fab(n): if n<3: return n return fa ...

随机推荐

  1. angular js 和 dajango 标签{{}} 冲突

    问题描述: 如果在django的模板中使用{{ }},不会被angularjs 识别. 解决办法: >1.5 的django中,将需要angularjs解释的{{expression}}放在 v ...

  2. Ionic在Android上部署app步骤

    详情链接:http://www.ncloud.hk/%E6%8A%80%E6%9C%AF%E5%88%86%E4%BA%AB/ionic%E5%9C%A8android%E4%B8%8A%E9%83% ...

  3. 在 Mac OS 上编译 FFmpeg

    本文转自:在 Mac OS 上编译 FFmpeg | www.samirchen.com 安装 Xcode 和 Command Line Tools 从 App Store 上安装 Xcode,并确保 ...

  4. 【转载】QT QTableView用法小结

    原始日期: 2016-08-16 09:28 来源:http://blog.csdn.net/wang_lichun/article/details/7805253 QTableView常用于实现数据 ...

  5. MySQL错误2003:Can't connect to MySQL server (10060)

    1.网络不通. 检查能不能ping通. 2.防火墙设置. 防火墙是否放过mysql的进程,是否屏蔽了mysql的3306端口. 3.mysql的账户设置. mysql账户是否不允许远程连接.如果无法连 ...

  6. C#调用TSC条码打印机打印条码

    #region 调用TSC打印机打印条码 /// <summary> /// 调用TSC打印机打印条码 /// </summary> /// <param name=&q ...

  7. android源码、博文2

      每周精选 第 54 期   精品源码 仿网易新闻app下拉标签选择菜单     仿网易新闻app下拉标签选择菜单,长按拖动排序,点击增删标签控件##示例 https://github.com/we ...

  8. Chrome浏览器扩展开发系列之十五:跨域访问的XMLHttpRequest对象

    XMLHttpRequest对象是W3C的标准API,用于访问服务器资源.XMLHttpRequest对象支持多种文本格式,如XML和JSON等.XMLHttpRequest对象可以通过HTTP和HT ...

  9. Java之分支和循环

    Java中的分支语句: if语句: if语句的四种写法: (1) if(表达式_布尔值) { ... } (2) if(表达式_布尔值) { ... } else { ... } (3) if(表达式 ...

  10. EventBus 事件总线之我的理解

    用例:假设公司发布了一个公告 需要通过短信 和 邮件分别2种方式 通知员工 1:首先我们建立领域模型 /// <summary> /// 领域核心基类 /// </summary&g ...