如何優雅地等待全部的goroutine退出

goroutine和channel是Go語言很是棒的特點,它們提供了一種很是輕便易用的併發能力。可是當您的應用進程中有不少goroutine的時候,如何在主流程中等待全部的goroutine 退出呢?併發

1 經過Channel傳遞退出信號

Go的一大設計哲學就是:經過Channel共享數據,而不是經過共享內存共享數據。主流程能夠經過channel向任何goroutine發送中止信號,就像下面這樣:post

func run(done chan int) {
        for {
                select {
                case <-done:
                        fmt.Println("exiting...")
                        done <- 1
                        break
                default:
                }

                time.Sleep(time.Second * 1)
                fmt.Println("do something")
        }
}

func main() {
        c := make(chan int)

        go run(c)

        fmt.Println("wait")
        time.Sleep(time.Second * 5)

        c <- 1
        <-c

        fmt.Println("main exited")
}

  

這種方式能夠實現優雅地中止goroutine,可是當goroutine特別多的時候,這種方式無論在代碼美觀上仍是管理上都顯得笨拙不堪。spa

2 使用waitgroup

sync包中的Waitgroup結構,是Go語言爲咱們提供的多個goroutine之間同步的好刀。下面是官方文檔對它的描述:設計

A WaitGroup waits for a collection of goroutines to finish. The main goroutine calls Add to set the number of goroutines to wait for. 
Then each of the goroutines runs and calls Done when finished. At the same time, Wait can be used to block until all goroutines have finished.

一般狀況下,咱們像下面這樣使用waitgroup:blog

  1. 建立一個Waitgroup的實例,假設此處咱們叫它wg
  2. 在每一個goroutine啓動的時候,調用wg.Add(1),這個操做能夠在goroutine啓動以前調用,也能夠在goroutine裏面調用。固然,也能夠在建立n個goroutine前調用wg.Add(n)
  3. 當每一個goroutine完成任務後,調用wg.Done()
  4. 在等待全部goroutine的地方調用wg.Wait(),它在全部執行了wg.Add(1)的goroutine都調用完wg.Done()前阻塞,當全部goroutine都調用完wg.Done()以後它會返回。

那麼,若是咱們的goroutine是一匹不知疲倦的牛,一直孜孜不倦地工做的話,如何在主流程中告知並等待它退出呢?像下面這樣作:進程

type Service struct {
        // Other things

        ch        chan bool
        waitGroup *sync.WaitGroup
}

func NewService() *Service {
	s := &Service{
                // Init Other things
                ch:        make(chan bool),
                waitGroup: &sync.WaitGroup{},
	}

	return s
}

func (s *Service) Stop() {
        close(s.ch)
        s.waitGroup.Wait()
}

func (s *Service) Serve() {
        s.waitGroup.Add(1)
        defer s.waitGroup.Done()

        for {
                select {
                case <-s.ch:
                        fmt.Println("stopping...")
                        return
                default:
                }
                s.waitGroup.Add(1)
                go s.anotherServer()
	}
}
func (s *Service) anotherServer() {
        defer s.waitGroup.Done()
        for {
                select {
                case <-s.ch:
                        fmt.Println("stopping...")
                        return
                default:
                }

                // Do something
        }
}

func main() {

        service := NewService()
        go service.Serve()

        // Handle SIGINT and SIGTERM.
        ch := make(chan os.Signal)
        signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
        fmt.Println(<-ch)

        // Stop the service gracefully.
        service.Stop()
}

  

是否是方便優雅多了?內存

Author: Cobbliu文檔

Created: 2015-04-28 Tue 00:24get

Emacs 24.4.1 (Org mode 8.2.10)同步

相關文章
相關標籤/搜索