2012-11-22 15 views
5

我正在嘗試實施一個字數統計程序,但第一步我遇到了一些問題。Golang goroutine不會與頻道內部運行

這裏是我的代碼:

package main 

import (
    "fmt" 
    "os" 
    "bufio" 
    "sync" 
) 

// Load data into channel 
func laodData(arr []string,channel chan string,wg sync.WaitGroup) { 
    for _,path := range arr { 
     file,err := os.Open(path) 
     fmt.Println("begin to laodData ", path) 
     if err != nil { 
      fmt.Println(err) 
      os.Exit(-1) 
     } 
     defer file.Close() 
     reader := bufio.NewReaderSize(file, 32*10*1024) 
     i := 0 
     for { 
      line,err := reader.ReadString('\n') 
      channel <- line 
      if err != nil { 
       break 
      } 
      i++ 
      if i%200 == 0 { 
       fmt.Println(i," lines parsed") 
      } 
     } 
     fmt.Println("finish laodData ", path) 
    } 
    wg.Done() 
} 

// dispatch data lines into different mappers 
func dispatcher(channel chan string,wg sync.WaitGroup){ 
    fmt.Println("pull data 11") 
    line,ok := <- channel 
    fmt.Println(ok) 
    for ok { 
     fmt.Println(line) 
     line,ok = <- channel 
    } 
    fmt.Println("pull data 22") 
    wg.Done() 
} 

func main() { 
    path := os.Args 
    if len(path) < 2 { 
     fmt.Println("Need Input Files") 
     os.Exit(0) 
    } 
    var wg sync.WaitGroup 
    wg.Add(2) 

    channel := make(chan string) 
    defer close(channel) 

    fmt.Println("before dispatcher") 
    go laodData(path[1:],channel,wg) 
    go dispatcher(channel,wg) 
    wg.Wait() 

    fmt.Println("after dispatcher") 
} 

這是我的輸出:

... 

finish laodData result.txt 

throw: all goroutines are asleep - deadlock! 

goroutine 1 [semacquire]: 
sync.runtime_Semacquire(0x42154100, 0x42154100) 
    /usr/local/go/src/pkg/runtime/zsema_amd64.c:146 +0x25 
sync.(*WaitGroup).Wait(0x4213b440, 0x0) 
    /usr/local/go/src/pkg/sync/waitgroup.go:79 +0xf2 
main.main() 
    /Users/kuankuan/go/src/mreasy/main.go:66 +0x238 

goroutine 2 [syscall]: 
created by runtime.main 
    /usr/local/go/src/pkg/runtime/proc.c:221 

goroutine 4 [chan receive]: 
main.dispatcher(0x42115a50, 0x0, 0x2, 0x0) 
    /Users/kuankuan/go/src/mreasy/main.go:45 +0x223 
created by main.main 
    /Users/kuankuan/go/src/mreasy/main.go:65 +0x228 
exit status 2 

謝謝!

回答

8

程序在主要goroutine退出時終止,因此dispatcher()沒有時間做任何事情。您需要阻止main(),直至dispatcher()完成。通道可以用於此:

package main 

import (
    "fmt" 
    "os" 
    "bufio" 
) 

var done = make(chan bool)    // create channel 

// Load files and send them into a channel for mappers reading. 
func dispatcher(arr []string,channel chan string) { 
    for _,path := range arr { 
     file,err := os.Open(path) 
     fmt.Println("begin to dispatch ", path) 
     if err != nil { 
      fmt.Println(err) 
      os.Exit(-1) 
     } 
     defer file.Close() 
     reader := bufio.NewReaderSize(file, 32*10*1024) 
     i := 0 
     for { 
      line,_ := reader.ReadString('\n') 
      channel <- line 
      i++ 
      if i%200 == 0 { 
       fmt.Println(i," lines parsed") 
      } 
     } 
     fmt.Println("finish dispatch ", path) 
    } 
    done <- true     // notify main() of completion 
} 

func main() { 
    path := os.Args 
    if len(path) < 2 { 
     fmt.Println("Need Input Files") 
     os.Exit(0) 
    } 
    channel := make(chan string) 
    fmt.Println("before dispatcher") 
    go dispatcher(path[1:],channel) 
    <-done     // wait for dispatcher() 
    fmt.Println("after dispatcher") 
} 
+2

在這種精確的情況下,去掉'go dispatcher(path [1:],channel)'中的'go'會更簡單。 –

+0

謝謝@dystory,我需要在主線程旁邊的調度員旁邊做其他事情。 – MrROY

+0

嗨,勝利者,我遵循你的建議,但我遇到了死鎖!問題。**拋出:所有的goroutines都睡着了 - 僵局! channel < - line ** – MrROY

2

modified your example在旅途中操場那裏沒有文件I/O的運行;它會在通道上發送隨機數字。

@Victor Deryagin的解釋和他對使用「完成」通道的建議是正確的。你陷入僵局的原因是你的goroutine在頻道上發送,但是沒有人讀取它,所以程序停留在這一點上。在上面的鏈接中,我添加了一個消費者例程。程序然後按預期同時運行。

請注意,要等待幾個goroutines,使用sync.WaitGroup更清晰易用。

+0

我給頻道添加了一個接收器,但它仍然死鎖... – MrROY

1

原始問題需要修正兩個問題。

  1. 您必須在完成發送所有數據後關閉通道。在func laodData,請使用關閉(頻道)發送所有數據。
  2. 通過sync.Waitgroup作爲參考。您將wg作爲參數中的值發送給以下函數... laodData和調度程序函數。

解決這兩個問題將解決您的死鎖問題。代碼中出現死鎖的原因如下:

  • 離開發送通道未關閉將導致下行通道等待較長的時間。
  • 發送參數sync.Waitgroup作爲值。它應該作爲參考發送,否則它將創建您要發送的對象的新副本。