2012-11-12 33 views
10

Tôi bắt đầu học cách học ngôn ngữ ngày trước. Khi tôi cố bắt đầu viết một số mã vui, tôi bị mắc kẹt bởi một hành vi kỳ lạ.chức năng đệ quy trong ngôn ngữ đi

package main 

import "fmt" 

func recv(value int) { 
    if value < 0 { 
     return 
    } 

    fmt.Println(value) 
    go recv(value-1) 
} 

func main() { 
    recv(10) 
} 

khi tôi chạy mã trên, chỉ 10 được in. Khi tôi xóa go trước khi gọi tới recv, 10 đến 0 được in ra. Tôi tin rằng tôi đang lạm dụng thói quen đi làm ở đây, nhưng tôi không thể hiểu tại sao nó thất bại bắt đầu một thói quen đi theo cách này.

Trả lời

15

Khi chức năng chính được trả về, Go sẽ không đợi cho bất kỳ goroutines nào hiện có để kết thúc mà thay vào đó chỉ cần thoát.

recv sẽ trở lại chính sau "lần lặp đầu tiên" và vì chính không còn gì để làm nữa, chương trình sẽ chấm dứt.

Một giải pháp cho vấn đề này là phải có một kênh báo hiệu rằng tất cả các công việc được thực hiện, như sau:

package main 

import "fmt" 

func recv(value int, ch chan bool) { 
    if value < 0 { 
     ch <- true 
     return 
    } 

    fmt.Println(value) 
    go recv(value - 1, ch) 
} 

func main() { 
    ch := make(chan bool) 
    recv(10, ch) 

    <-ch 
} 

Ở đây, recv sẽ gửi một boolean đơn trước khi trở về, và main sẽ đợi cho điều đó tin nhắn trên kênh.

Đối với logic của chương trình, việc bạn sử dụng loại giá trị hoặc giá trị cụ thể nào không quan trọng. booltrue chỉ là một ví dụ đơn giản. Nếu bạn muốn hiệu quả hơn, sử dụng chan struct{} thay vì chan bool sẽ giúp bạn tiết kiệm thêm một byte, vì các cấu trúc trống không sử dụng bất kỳ bộ nhớ nào.

+4

Đối với kênh tín hiệu, nơi dữ liệu kênh không quan trọng, bạn có thể sử dụng 'chan struct {}'. Một cấu trúc rỗng không chiếm bộ nhớ, trong khi vẫn cho phép hành vi mong muốn. Trong khi một boolean chiếm một byte. – jimt

+1

Vâng, thành thật mà nói tôi đã đi cho một bool để không làm cho nó phức tạp hơn. Nhưng vâng, một cấu trúc rỗng là kỹ thuật tốt hơn. Bạn có muốn kết hợp nó trong câu trả lời của tôi hay tôi sẽ tự chỉnh sửa nó? :) –

+1

Đó là câu trả lời của bạn, hãy chọn nó :) – jimt

10

A sync.Waitgroup là một giải pháp khác và dành riêng cho mục đích chờ đợi số lượng goroutines tùy ý để chạy khóa học của họ.

package main 

import (
    "fmt" 
    "sync" 
) 

func recv(value int, wg *sync.WaitGroup) { 
    if value < 0 { 
     return 
    } 

    fmt.Println(value) 

    wg.Add(1) // Add 1 goroutine to the waitgroup. 

    go func() { 
     recv(value-1, wg) 
     wg.Done() // This goroutine is finished. 
    }() 
} 

func main() { 
    var wg sync.WaitGroup 
    recv(10, &wg) 

    // Block until the waitgroup signals 
    // all goroutines to be finished. 
    wg.Wait() 
} 
-2

Tôi đã làm như vậy và cũng đã làm việc. Làm thế nào mà?

package main 

import "fmt" 

func recv(value int) { 
    if value < 0 { 
     return 
    } 

    fmt.Println(value) 
    recv(value - 1) 
} 

func main() { 
    recv(10) 
} 
+0

Phiên bản thứ hai của bạn có in được 10,9,8,7,6,5,4,3,2,1 không? Hãy thử 100 lần: D – vrbilgi

+1

@vrbilgi Nó không hoạt động. Tôi đã lầm. Cảm ơn U. Đã chỉnh sửa. ;) – rplaurindo

Các vấn đề liên quan