goroutine 加 channel 代替递归调用,突破递归调用的层级限制

 1 package main
 2 
 3 import (
 4     "fmt"
 5     "github.com/davecgh/go-spew/spew"
 6     "github.com/BurntSushi/toml"
 7     "errors"
 8     "sync"
 9 )
10 
11 type kvData struct {
12     Prefix string // 前缀
13     Data map[string]interface{}
14 }
15 
16 // 递归将所有的层级遍历出来
17 func unmarshal(m kvData, work chan kvData, result chan []map[string]interface{}, wg *sync.WaitGroup) (error) {
18     defer wg.Done()
19     var r []map[string]interface{}
20     for k, v := range m.Data {
21         switch v.(type) {
22         case string, int64, float64, bool:
23             r = append(r, map[string]interface{}{"k":m.Prefix + k, "v":v})
24         case map[string]interface{}:
25             wg.Add(2)
26             work <- kvData{Prefix:m.Prefix + k + ".", Data : v.(map[string]interface{})}
27         default:
28             return errors.New("目前只能识别string、int、float、bool类型的配置")
29         }
30     }
31 
32     result <- r
33 
34     return nil
35 }
36 
37 func main() {
38     text := `
39 myip = "1.1.4.51"
40 type = "red"
41 [server]
42   [server.http]
43     addr="1.11.7.1:5"
44   [server.grpc]
45     addr="1.1.1.1:5"`
46 
47     var obj map[string]interface{}
48 
49     if e := toml.Unmarshal([]byte(text), &obj); e != nil {
50         spew.Dump(e)
51     }
52 
53 
54 
55     var work = make(chan kvData, 30)
56     var result = make(chan []map[string]interface{}, 30)
57     var wg sync.WaitGroup
58 
59     var r []map[string]interface{}
60 
61     wg.Add(2)
62     if e := unmarshal(kvData{Prefix:"", Data:obj}, work, result, &wg); e != nil {
63         fmt.Println(e)
64         return
65     }
66 
67     var end = make(chan int)
68     go func() {
69         for {
70             select {
71             case newWork := <-work:
72                 fmt.Println("w")
73                 spew.Dump(newWork)
74                 go unmarshal(newWork, work, result, &wg)
75             case newResult := <-result:
76                 wg.Done()
77                 fmt.Println("r")
78                 spew.Dump(newResult)
79                 if len(newResult) != 0 {
80                     r = append(r, newResult...)
81                 }
82             case <-end:
83                 spew.Dump(r)
84                 return
85             }
86         }
87     }()
88     wg.Wait()
89     end<-1
90 
91     fmt.Println("--all----\n")
92     for _, v := range r {
93         fmt.Println(" k => ", v["k"])
94         fmt.Println(" v => ", v["v"])
95     }
96 
97     return
98 }

 

1、创建两个channel(work,result)分别用来存放任务、返回结果。
2、创建一个结构体 kvData 来存放任务以及任务执行的环境依赖。
3、创建sync.WaitGroup 来等待所有的 goroutine 执行完成。

限制递归层级的原因就是递归的栈的释放是从最后一层倒退着向上释放的,其实限制递归层级的条件就是栈的容量。
goroutine 加 channel 在没层级的过程中都会将结果放回到结果的channel(result)、如果需要进一步分析就将需要进一步分析的任务放到 channel(work)中
一方面是并发在执行、一方面是不会造成栈的累积,因此不存在层级的限制。

注:
1、递归在数量比较少的时候速度和内存的占用量是比较好的(没有做具体的实验所有没有具体的数据)
2、goroutine 启动的时候大概需要占用4K的内容,针对于这样的递归来说还是比较大的一个开销
3、goroutine 应该做一个pool,然后反复使用

posted @ 2017-08-03 21:36  想飞的枫叶  阅读(415)  评论(0编辑  收藏  举报