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. Javascript 严格模式use strict

    一.概述 除了正常运行模式,ECMAscript 5添加了第二种运行模式:“严格模式”(strict mode).顾名思义,这种模式使得Javascript在更严格的条件下运行. 设立”严格模式”的目 ...

  2. Linux配置浮动IP实现WEB高可用

    在高可用集群环境中,一般都需要使用浮动IP来实现web高可用(High Availability). 浮动IP的概念以及为什么需要浮动IP请参考:浮动IP(FLOAT IP) 本篇文章主要讲实际操作步 ...

  3. Index Scans 索引扫描

    官方文档链接地址 http://docs.oracle.com/cd/E11882_01/server.112/e40540/indexiot.htm#CNCPT1170 Index Scans 在索 ...

  4. 去掉CI框架默认url中的index.php

    1:.htaccess //放置在根目录下,和入口文件index.php的同级目录<IfModule mod_rewrite.c>RewriteEngine onRewriteCond % ...

  5. 自动化测试—monkeyrunner

    步骤:     1. 在 pycharm 中编写一个 python的脚本,注意:在运行脚本时不要有注释,不然会报错                 2. 在 dos 窗口中运行脚本.         ...

  6. 在vs2010中显示代码的行数

    1.打开VS2010,然后"工具" → "选项" 2.在选项页面,点击"文本编辑器"→"所有语言",在显示里将[行号]选 ...

  7. Oracle 使用命令导入dmp文件

    若要导入到特定的表空间则需要新建表空间,若不要求,则用已有的,则只需执行下面的步骤 在dos窗口中输入imp 用户名/密码@ip地址:端口号/数据库实例 file='需要导入的dmp文件的路径' fu ...

  8. centos 7 安装MySQL 5.6

    本文根据mysql的官方文档操作:https://dev.mysql.com/doc/mysql-yum-repo-quick-guide/en/ 由于Centos7 默认数据库是mariabd(网上 ...

  9. ActiveMQ 503错误

    问题描述: 在Linux系统下安装ActiveMQ,启动服务 正常启动后,通过浏览器进行访问 可以正常显示home页面,但是点击其他菜单,如Queues,Topics等,都会出现503错误,如图 问题 ...

  10. JavaWeb 后端 <十四> 文件上传下载

    1.文件上传与下载 案例: 注册表单/保存商品等相关模块! --à 注册选择头像 / 商品图片 (数据库:存储图片路径 / 图片保存到服务器中指定的目录) 1.1 文件上传 文件上传,要点: 前台: ...