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/