Last active
February 5, 2021 02:45
-
-
Save xin053/b2b0b4264443592b685d50151c061937 to your computer and use it in GitHub Desktop.
[go 陷阱]go 陷阱 #go #陷阱
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 闭包 trap | |
var rmdirs []func() | |
for _, d := range tempDirs() { | |
dir := d // 不能删除,否则循环删除的是同一个目录 | |
os.MkdirAll(dir, 0755) | |
rmdirs = append(rmdirs, func() { | |
os.RemoveAll(dir) | |
}) | |
} | |
for _, rmdir := range rmdirs { | |
rmdir() | |
} | |
// 因为 for 循环引入了新的词法块, d 在这个词法块中被声明,而闭包(函数值)中记录的是循环变量的内存地址,而不是某一时刻的值,所以引入一个新的变量,将 d 赋值给 dir | |
// 这并不是循环的问题,而是闭包(函数值)等待循环结束之后才执行 | |
// 另外的解决方法,将 d 作为参数传递给闭包(函数值) | |
for _, dir := range tempDirs() { | |
os.MkdirAll(dir, 0755) | |
rmdirs = append(rmdirs, func(dir string) { | |
os.RemoveAll(dir) | |
}(dir)) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// defer problem | |
for i := 0; i < 3; i++ { | |
defer func() { | |
fmt.Println("b:", i) | |
}() | |
} | |
// fix1 | |
for i := 0; i < 3; i++ { | |
defer func(i int) { | |
fmt.Println("b:", i) | |
}(i) | |
} | |
// fix2 | |
for i := 0; i < 3; i++ { | |
i := i | |
defer func() { | |
fmt.Println("b:", i) | |
}() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// go routine problem | |
for i := 0; i < 3; i++ { | |
go func() { | |
fmt.Println("b:", i) | |
}() | |
} | |
// fix1 | |
for i := 0; i < 3; i++ { | |
go func(i int) { | |
fmt.Println("b:", i) | |
}(i) | |
} | |
// fix2 | |
for i := 0; i < 3; i++ { | |
i := i | |
go func() { | |
fmt.Println("b:", i) | |
}() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func main() { | |
t := T{y: [3]int16{123, 456, 789}} | |
p := unsafe.Pointer(&t) | |
// ty2 := (*int16)(unsafe.Pointer(uintptr(p)+N+M+M)) | |
addr := uintptr(p) + N + M + M | |
// Now the t value becomes unused, its memory may be | |
// garbage collected at this time. So the following | |
// use of the address of t.y[2] may become invalid | |
// and dangerous! | |
ty2 := (*int16)(unsafe.Pointer(addr)) | |
fmt.Println(*ty2) | |
} | |
// 解决方法 | |
ty2 := (*int16)(unsafe.Pointer(uintptr(p)+N+M+M)) | |
// or | |
func main() { | |
t := T{y: [3]int16{123, 456, 789}} | |
p := unsafe.Pointer(t) | |
addr := uintptr(p) + N + M + M | |
ty2 := (*int16)(unsafe.Pointer(addr)) | |
// This following line ensures the memory of | |
// the value t will not get garbage collected | |
// currently for sure. | |
runtime.KeepAlive(p) | |
fmt.Println(*ty2) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// for 循环多次 defer,导致栈持续增长,占用内存过大 | |
func writeManyFiles(files []File) error { | |
for _, file := range files { | |
f, err := os.Open(file.path) | |
if err != nil { | |
return err | |
} | |
defer f.Close() | |
_, err = f.WriteString(file.content) | |
if err != nil { | |
return err | |
} | |
err = f.Sync() | |
if err != nil { | |
return err | |
} | |
} | |
return nil | |
} | |
// 可将 defer 放到匿名函数中解决 | |
func writeManyFiles(files []File) error { | |
for _, file := range files { | |
if err := func() error { | |
f, err := os.Open(file.path) | |
if err != nil { | |
return err | |
} | |
// The close method will be called at | |
// the end of the current loop step. | |
defer f.Close() | |
_, err = f.WriteString(file.content) | |
if err != nil { | |
return err | |
} | |
return f.Sync() | |
}(); err != nil { | |
return err | |
} | |
} | |
return nil | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import "time" | |
func main() { | |
for { | |
select { | |
case <-time.After(3 * time.Second): | |
default: | |
} | |
} | |
} | |
// After 产生的 timer 需要到达 3s 之后才会释放, 而短时间内 for 循环会产生大量 timer, 这些 timer 都不会立即释放,所以程序占用很大内存,需要在 timer 不需要时手动释放, fix 如下 | |
package main | |
import "time" | |
func main() { | |
for { | |
t := time.NewTimer(3*time.Second) | |
select { | |
case <- t.C: | |
default: | |
t.Stop() | |
} | |
} | |
} | |
// 或者如果不想每次 for 中 new 一个 timer,可以使用 timer.Reset 来实现 | |
func longRunning(messages <-chan string) { | |
timer := time.NewTimer(time.Minute) | |
defer timer.Stop() //A time.Timer value can be leaved in non-stopping status when it is not used any more, but it is recommended to stop it in the end. | |
for { | |
select { | |
case <-timer.C: // expires (timeout) | |
return | |
case msg := <-messages: | |
fmt.Println(msg) | |
// This "if" block is important. | |
if !timer.Stop() { | |
<-timer.C // 如果 Stop() 返回 false(timer 到期), 那么清空 C 中的值,因为 Reset 并不会清空已经发送到 C 中的值 | |
} | |
} | |
// Reset to reuse. | |
timer.Reset(time.Minute) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Except sometimes we deliberately let the main goroutine in a program hanging to avoid the program exiting, most other hanging goroutine cases are unexpected. It is hard for Go runtime to judge whether or not a goroutine in blocking state is hanging or stays in blocking state temporarily, so Go runtime will never release the resources consumed by a hanging goroutine. | |
// In the first-response-wins channel use case, if the capacity of the channel which is used a future is not large enough, some slower response goroutines will hang when trying to send a result to the future channel. For example, if the following function is called, there will be 4 goroutines stay in blocking state for ever. | |
func request() int { | |
c := make(chan int) | |
for i := 0; i < 5; i++ { | |
i := i | |
go func() { | |
c <- i // 4 goroutines will hang here. | |
}() | |
} | |
return <-c | |
} | |
// To avoid the four goroutines hanging, the capacity of channel c must be at least 4. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// In practice, values of the types (except the Locker interface values) in the sync standard package should never be copied. We should only copy pointers of such values. | |
// The following is bad concurrent programming example. In this example, when the Counter.Value method is called, a Counter receiver value will be copied. As a field of the receiver value, the respective Mutex field of the Counter receiver value will also be copied. The copy is not synchronized, so the copied Mutex value might be corrupted. Even if it is not corrupted, what it protects is the use of the copied field n, which is meaningless generally. | |
import "sync" | |
type Counter struct { | |
sync.Mutex | |
n int64 | |
} | |
// This method is okay. | |
func (c *Counter) Increase(d int64) (r int64) { | |
c.Lock() | |
c.n += d | |
r = c.n | |
c.Unlock() | |
return | |
} | |
// The method is bad. When it is called, | |
// the Counter receiver value will be copied. | |
func (c Counter) Value() (r int64) { | |
c.Lock() | |
r = c.n | |
c.Unlock() | |
return | |
} | |
// We should change the receiver type of the Value method to the pointer type *Counter to avoid copying sync.Mutex values. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var s0 string // a package-level variable | |
// A demo purpose function. | |
func f(s1 string) { | |
s0 = s1[:50] | |
// Now, s0 shares the same underlying memory block | |
// with s1. Although s1 is not alive now, but s0 | |
// is still alive, so the memory block they share | |
// couldn't be collected, though there are only 50 | |
// bytes used in the block and all other bytes in | |
// the block become unavailable. | |
} | |
func demo() { | |
s := createStringWithLengthOnHeap(1 << 20) // 1M bytes | |
f(s) | |
} | |
// fix | |
func f(s1 string) { | |
s0 = string([]byte(s1[:50])) | |
} | |
// or | |
func f(s1 string) { | |
s0 = strings.Repeat(s1[:50], 1) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Similarly to substrings, subslices may also cause kind-of memory leaking. In the following code, after the g function is called, most memory occupied by the memory block hosting the elements of s1 will be lost (if no more values reference the memory block). | |
var s0 []int | |
func g(s1 []int) { | |
// Assume the length of s1 is much larger than 30. | |
s0 = s1[len(s1)-30:] | |
} | |
// fix | |
func g(s1 []int) { | |
s0 = append([]int(nil), s1[len(s1)-30:]...) | |
// Now, the memory block hosting the elements | |
// of s1 can be collected if no other values | |
// are referencing the memory block. | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment