被 Map 遍历打了脸
Map 遍历顺序是乱序的
var convertJavaTimeFormat = map[string][string] { "yyyy": "2006", "yy": "06", "MM": "01", "dd": "02", } // ConvertTimeFormat formats t with java style format like yyyy-MM-dd. func ConvertTimeFormat(t time.Time, javaStyleFmt string) string { s := javaStyleFmt for k, v := range convertJavaTimeFormat { s = strings.ReplaceAll(s, k, v) } return t.Format(s) }
yyyyyyyyyyyyyyyy-MM-dd2021-06-082121-06-08
When iterating over a map with a range loop, the iteration order is not specified and is not guaranteed to be the same from one iteration to the next. If you require a stable iteration order you must maintain a separate data structure that specifies that order.
示例,playground,代码拷贝自这里
package main import "fmt" func main() { blogArticleViews := map[string]int{ "unix": 0, "python": 1, "go": 2, "javascript": 3, "testing": 4, "philosophy": 5, "startups": 6, "productivity": 7, "hn": 8, "reddit": 9, "C++": 10, } for key, views := range blogArticleViews { fmt.Println("There are", views, "views for", key) } }
你猜想会出现下面这样吧:
There are 0 views for unix
There are 1 views for python
There are 2 views for go
There are 3 views for javascript
There are 4 views for testing
There are 5 views for philosophy
There are 6 views for startups
There are 7 views for productivity
There are 8 views for hn
There are 9 views for reddit
There are 10 views for C++
实际上呢,可能是这样的:
There are 3 views for javascript
There are 5 views for philosophy
There are 10 views for C++
There are 0 views for unix
There are 1 views for python
There are 2 views for go
There are 4 views for testing
There are 6 views for startups
There are 7 views for productivity
There are 8 views for hn
There are 9 views for reddit
改版如下:
var convertJavaTimeFormat = []string { "yyyy", "2006", "yy", "06", "MM", "01", "dd", "02", } // ConvertTimeFormat formats t with java style format like yyyy-MM-dd. func ConvertTimeFormat(t time.Time, javaStyleFmt string) string { s := javaStyleFmt k := convertJavaTimeFormat for i:=0; i+1 < len(k); i+=2 { s = strings.ReplaceAll(s, k[i], k[i+1]) } return t.Format(s) }
被切片切了一刀
package main import ( "fmt" ) func main() { var a []byte a = append(a, []byte(`012345678`)...) fmt.Println(`len(a):`, len(a), `cap(a):`, cap(a)) fmt.Printf("a: %s, a's addr %p\n", a, &a[0]) b := append(a[:3], []byte(`...`)...) fmt.Println(`len(b):`, len(b), `cap(b):`, cap(b)) fmt.Println(`len(a):`, len(a), `cap(a):`, cap(a)) fmt.Printf("a: %s, a's addr %p, b: %s, b's addr %p\n", a, &a[0], b, &b[0]) }
输出结果
len(a): 9 cap(a): 16
a: 012345678, a's addr 0xc0000ae010
len(b): 6 cap(b): 16
len(a): 9 cap(a): 16
a: 012...678, a's addr 0xc0000ae010, b: 012..., b's addr 0xc0000ae010
被头等函数撞了一下头
package main import ( "fmt" "sync" ) func cloneMap(src map[string]bool) map[string]bool { dest := make(map[string]bool) for k, v := range src { dest[k] = v } return dest } func NewHandler() func(string) { m := make(map[string]bool) return func(v string) { m = cloneMap(m) m[v] = true } } func main() { var wg sync.WaitGroup h := NewHandler() for i := 0; i < 100; i++ { wg.Add(1) go func(index int) { defer wg.Done() for j := 0; j < 10000; j++ { h(fmt.Sprintf("Index:%d", index)) } }(i) } wg.Wait() }
跑的结果是
fatal error: concurrent map iteration and map write
fatal error: concurrent map iteration and map write
也是不明所以,丈二和尚摸不着头脑,查了一会,才找到问题根源所在。
m := cloneMap(m)
for select default 空转忙
httpdump 说线上 CPU 都 100% 以上,本地跑一下,果然如此,赶紧上 pprof,一查一个准