1、记一次gorountine导致的泄漏
在项目中使用https://github.com/deckarep/golang-set这个三方包造成了gorountine泄漏。先来看一下这个包的迭代器设置,如下:
package mapset
// Iterator defines an iterator over a Set, its C channel can be used to range over the Set's
// elements.
type Iterator struct {
C <-chan interface{}
stop chan struct{}
}
// Stop stops the Iterator, no further elements will be received on C, C will be closed.
func (i *Iterator) Stop() {
// Allows for Stop() to be called multiple times
// (close() panics when called on already closed channel)
defer func() {
recover()
}()
close(i.stop)
// Exhaust any remaining elements.
for range i.C {
}
}
// newIterator returns a new Iterator instance together with its item and stop channels.
func newIterator() (*Iterator, chan<- interface{}, <-chan struct{}) {
itemChan := make(chan interface{})
stopChan := make(chan struct{})
return &Iterator{
C: itemChan,
stop: stopChan,
}, itemChan, stopChan
}
这样,向外提供API时的代码如下:
func (set *threadSafeSet) Iterator() *Iterator {
iterator, ch, stopCh := newIterator()
go func() {
set.RLock()
L:
for elem := range set.s {
select {
case <-stopCh:
break L
case ch <- elem:
}
}
close(ch)
set.RUnlock()
}()
return iterator
}
正确的使用方法如下:
type YourType struct {
Name string
}
func ExampleIterator() {
set := NewSetFromSlice([]interface{}{
&YourType{Name: "Alise"},
&YourType{Name: "Bob"},
&YourType{Name: "John"},
&YourType{Name: "Nick"},
})
var found *YourType
it := set.Iterator()
for elem := range it.C {
if elem.(*YourType).Name == "John" {
found = elem.(*YourType)
it.Stop()
}
}
fmt.Printf("Found %+v\n", found)
// Output: Found &{Name:John}
}
还有另外一个方法,如下:
func (set *threadSafeSet) Iter() <-chan interface{} {
ch := make(chan interface{})
go func() {
set.RLock()
for elem := range set.s {
ch <- elem
}
close(ch)
set.RUnlock()
}()
return ch
}
这个方法必须遍历完所有的elem元素,否则会造成gorountine阻塞。所以切不可在循环之内break。
2、理解chan chan类型
一个小Demo有助于理解,代码如下:
import (
"time"
"fmt"
)
func main() {
// make the request chan chan that both go-routines will be given
requestChan := make(chan chan string)
// start the goroutines
go goroutineC(requestChan)
go goroutineD(requestChan)
// sleep for a second to let the goroutines complete
time.Sleep(time.Second)
}
func goroutineC(requestChan chan chan string) {
// make a new response chan
responseChan := make(chan string)
// send the responseChan to goRoutineD
requestChan <- responseChan
// read the response
response := <-responseChan
fmt.Printf("Response: %v\n", response)
}
func goroutineD(requestChan chan chan string) {
// read the responseChan from the requestChan
responseChan := <-requestChan
// send a value down the responseChan
responseChan <- "wassup!"
}
chan chan类型有非常大的用处,如实现每分钟百万流量的处理。
参考:
(1)http://tleyden.github.io/blog/2013/11/23/understanding-chan-chans-in-go/
(2)https://www.goin5minutes.com/blog/channel_over_channel/
(3)http://marcio.io/2015/07/handling-1-million-requests-per-minute-with-golang/
有疑问加站长微信联系(非本文作者)