2012-11-22 28 views
5

Tôi đang cố gắng triển khai chương trình đếm từ, nhưng với bước đầu tiên tôi gặp phải một số vấn đề.Golang goroutine không chạy với kênh bên trong

Dưới đây là mã của tôi:

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") 
} 

Và đây là kết quả của tôi:

... 

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 

Cảm ơn!

Trả lời

8

Chương trình chấm dứt khi thoát khỏi goroutine chính, do đó dispatcher() không có thời gian để làm bất cứ điều gì. Bạn cần chặn trong main() cho đến khi dispatcher() hoàn tất. Kênh có thể được sử dụng cho việc này:

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

Trong trường hợp chính xác này, việc xóa 'go' trong' go dispatcher (đường dẫn [1:], kênh) 'sẽ đơn giản hơn. –

+0

thanks @dystory, tôi cần phải làm điều gì đó khác bên cạnh điều phối trong chủ đề chính. – MrROY

+0

hi, victor, tôi đã theo lời khuyên của bạn, nhưng tôi đã gặp một Dead Lock! vấn đề. ** ném: tất cả các goroutines đang ngủ - bế tắc! kênh <- line ** – MrROY

2

I modified your example để chạy trên sân chơi Go nơi không có tệp I/O; Thay vào đó, nó sẽ gửi số ngẫu nhiên trên kênh.

@Giải thích của Deryagin và đề xuất của ông về việc sử dụng kênh "đã hoàn tất" là chính xác. Lý do bạn bị bế tắc là goroutine của bạn gửi trên kênh, nhưng không ai đọc từ nó, do đó chương trình bị kẹt tại thời điểm này. Trong liên kết ở trên, tôi đã thêm một goroutine người tiêu dùng. Chương trình sau đó chạy đồng thời như dự định.

Lưu ý rằng để chờ một số goroutines, việc sử dụng sync.WaitGroup rõ ràng hơn và dễ dàng hơn.

+0

Tôi thêm người nhận vào kênh, nhưng nó vẫn bị khóa ... – MrROY

1

Hai vấn đề cần được khắc phục trong câu hỏi gốc.

  1. Bạn phải đóng kênh khi bạn đã gửi xong tất cả dữ liệu. Trong func laodData, vui lòng sử dụng bài đăng gần (kênh) gửi tất cả dữ liệu.
  2. Vượt qua sync.Waitgroup làm tham chiếu.bạn đang gửi wg làm giá trị trong đối số cho các chức năng sau ... laodData và chức năng điều phối.

Sửa hai vấn đề này sẽ khắc phục vấn đề của bạn trong bế tắc. Lý do cho bế tắc trong mã của bạn theo:

  • Rời kênh gửi không được tiết lộ sẽ khiến kênh hạ lưu phải đợi trong thời gian dài.
  • gửi đối số là sync.Waitgroup làm giá trị. Nó sẽ được gửi như một tài liệu tham khảo nếu không nó sẽ tạo ra một bản sao mới của đối tượng mà bạn đang gửi.
Các vấn đề liên quan