2015-01-14 70 views
-1

我有以下代碼片段。終止第二個goroutine

package main 

import (
    "errors" 
    "fmt" 
    "time" 
) 

func errName(ch chan error) { 

    for i := 0; i < 10000; i++ { 

    } 

    ch <- errors.New("Error name") 
    close(ch) 
} 

func errEmail(ch chan error) { 

    for i := 0; i < 100; i++ { 

    } 
    ch <- errors.New("Error email") 
    close(ch) 
} 

func main() { 

    ch := make(chan error) 

    go errName(ch) 
    go errEmail(ch) 
    fmt.Println(<-ch) 
    //close(ch) 

    time.Sleep(1000000) 

} 

正如你所看到的,我讓兩個函數在goroutine,errName和errEmail中運行。我將錯誤類型作爲參數傳遞給一個通道。如果其中一人先完成,則應通過通道發送錯誤並關閉它。所以第二個,仍然運行的goroutine,已經不能再運行了,因爲我已經有錯誤了,我想終止仍在運行的goroutine。這是我在上面的示例中試圖達到的。

當我運行PROGRAMM,我有錯誤

panic: send on closed channel 

goroutine 6 [running]: 
main.errEmail(0xc0820101e0) 
     D:/gocode/src/samples/gorountine2.go:24 +0xfd 
created by main.main 
     D:/gocode/src/samples/gorountine2.go:33 +0x74 

goroutine 1 [runnable]: 
main.main() 
     D:/gocode/src/samples/gorountine2.go:34 +0xac 
exit status 2 

我知道,當我刪除了密切的語句,也不會驚慌,而是在運行的goroutine通道仍在等待錯誤引用和這意味着,它浪費了無用的內存(等待)。

當其中一人向通道發送錯誤時,第二個錯誤我將不在意了,那是我的目標。

回答

2

組織這次行爲的標準方法是使用

package main 

import (
    "fmt" 
    "time" 

    "code.google.com/p/go.net/context" 
) 

func errName(ctx context.Context, cancel context.CancelFunc) { 
    for i := 0; i < 10000; i++ { 
     select { 
     case <-ctx.Done(): 
      return 
     default: 
     } 
    } 
    cancel() 
} 

func errEmail(ctx context.Context, cancel context.CancelFunc) { 

    for i := 0; i < 100; i++ { 
     select { 
     case <-ctx.Done(): 
      return 
     default: 
     } 
    } 
    cancel() 
} 

func main() { 

    ctx := context.Background() 

    ctx, cancel := context.WithCancel(ctx) 

    go errName(ctx, cancel) 
    go errEmail(ctx, cancel) 

    <-ctx.Done() 

    if ctx.Err() != nil { 
     fmt.Println(ctx.Err()) 
    } 

    time.Sleep(1000000) 

} 

您可以就此事讀兩個很好的文章:

  1. http://blog.golang.org/context
  2. http://blog.golang.org/pipelines
+0

我讀了這些文章,但我不知道,如何使用它。謝謝你的例子,我會試試看。 –

+0

我不明白選擇語句和語句<-ctx.Done()。在主要功能中。他們在做什麼? –

+0

'select'是一項基本操作。閱讀併發教程以獲取更多信息。例如:http://www.golang-book.com/10/index.htm。您也可以聽一次談話:https://www.youtube.com/watch?v = f6kdp27TYZs – dyoo

2

使用另一個通道信號進行:

package main 

import (
    "errors" 
    "fmt" 
    "time" 
) 

func errName(ch chan error, done chan struct{}) { 
    for i := 0; i < 10000; i++ { 
     select { 
     case <-done: 
      fmt.Println("early return from name") 
      return 
     default: 
     } 
    } 
    select { 
    case: ch <- errors.New("Error name") 
    default: 
    } 
} 

func errEmail(ch chan error, done chan struct{}) { 
    for i := 0; i < 100; i++ { 
     select { 
     case <-done: 
      fmt.Println("early return from email") 
      return 
     default: 
     } 
    } 
    select { 
    case ch <- errors.New("Error email"): 
    default: 
    } 
} 

func main() { 
    ch := make(chan error, 1) 
    done := make(chan struct{}) 
    go errName(ch, done) 
    go errEmail(ch, done) 
    fmt.Println(<-ch) 
    close(done) 
    time.Sleep(1000000) 
} 

playground example

爲了防止丟失夠程從通道發送永遠阻擋,我創建了容量爲1的錯誤通道,並使用選擇發送時:

select { 
case ch <- errors.New("Error email"): 
default: 
} 

如果您正在使用多個級別的goroutine完成,那麼您應該考慮使用golang/x/net/context Context

1

Done chan struct{}提到(或其context.Context化身)是行爲的慣用和真實的方式。但簡單的方法來避免在你的片段恐慌可以

import "sync" 

var once sync.Once 

func errName(ch chan error) { 
    for i := 0; i < 10000; i++ { 

    } 
    once.Do(func() {ch <- errors.New("Error name"); close(ch)}()) 
} 
func errName(ch chan error) { 
    for i := 0; i < 10000; i++ { 

    } 
    once.Do(func() {ch <- errors.New("Error name"); close(ch)}()) 
}