2013-05-16 42 views
7

Tôi cần bắt đầu một lượng lớn goroutines và đợi cho đến khi chấm dứt. Cách trực quan dường như sử dụng một kênh để chờ đợi cho đến khi tất cả trong số họ đã kết thúc:Đợi chấm dứt n goroutines

package main 

type Object struct { 
    //data 
} 

func (obj *Object) Update(channel chan int) { 
    //update data 
    channel <- 1 
    return 
} 

func main() { 

    channel := make(chan int, n) 
    list := make([]Object, n, m) 
    for { 
     for _, object := range list { 
      go object.Update(channel) 
     } 
     for i := 0; i < n; i++ { 
      <-channel 
     } 
     //now everything has been updated. start again 
    } 
} 

Nhưng vấn đề là số lượng đối tượng và do đó số lượng goroutines có thể thay đổi. Có thể thay đổi dung lượng bộ đệm của kênh không?

Có cách nào thanh lịch hơn để thực hiện việc này không?

+2

Bạn có thể phân bổ lại mỗi lần lặp lại, nhưng bạn có thể muốn xem [WaitGroup] (http://golang.org/pkg/sync/#WaitGroup). – tjameson

+0

tjameson, cảm ơn sự giúp đỡ nhanh chóng. Điều đó có vẻ thực sự tốt. Bạn có thể muốn làm cho nó một câu trả lời. – lhk

+0

Xong, với ví dụ = D – tjameson

Trả lời

27

Tôi đã sử dụng WaitGroup làm giải pháp cho vấn đề này. Dịch mã hiện tại của bạn, với một số bản ghi để làm cho nó rõ ràng những gì đang xảy ra:

package main 

import "sync" 
import "fmt" 
import "time" 

type Object struct { 
    //data 
} 

func (obj *Object) Update(wg *sync.WaitGroup) { 
    //update data 
    time.Sleep(time.Second) 
    fmt.Println("Update done") 
    wg.Done() 
    return 
} 

func main() { 
    var wg sync.WaitGroup 
    list := make([]Object, 5) 
    for { 
     for _, object := range list { 
      wg.Add(1) 
      go object.Update(&wg) 
     } 
     //now everything has been updated. start again 
     wg.Wait() 
     fmt.Println("Group done") 
    } 
} 
+8

Câu trả lời hay! Tôi có thể đặt 'defer wg.Done()' vào lúc bắt đầu 'Cập nhật' mặc dù chỉ trong trường hợp hàm đó tăng và thu được lợi nhuận sớm vào một thời điểm nào đó trong tương lai. –

+0

Hoặc trong trường hợp có hoảng sợ hoặc một cái gì đó. – tjameson

4

Nhiệm vụ này không chính xác tầm thường, thật dễ dàng để viết một lỗi. Tôi khuyên bạn nên sử dụng một giải pháp đã sẵn sàng trong stdlib - sync.WaitGroup. Trích dẫn từ liên kết:

Nhóm chờ đợi chờ một bộ sưu tập các goroutin kết thúc. Goroutine chính gọi Add để thiết lập số lượng goroutine chờ đợi. Sau đó, mỗi goroutine chạy và gọi Xong khi hoàn tất. Đồng thời, Wait có thể được sử dụng để chặn cho đến khi tất cả các goroutine kết thúc.

+0

Và nếu số lượng goroutines chờ đợi không được biết trước? – Dfr

+0

@Dfr bạn tăng bộ đếm khi bạn khởi động mỗi goroutine, vì vậy giải pháp này vẫn là giải pháp tốt nhất khi bạn không biết số lượng goroutines bạn sẽ bắt đầu. – Awn

0

@tjameson đã làm một công việc tuyệt vời giải thích làm thế nào để sử dụng WaitGroup, làm thế nào để vượt qua một tham chiếu đến đối tượng WaitGroup của bạn để chức năng của bạn. Một thay đổi mà tôi sẽ thực hiện cho ví dụ của anh ta là đòn bẩy defer khi bạn đang Done. Tôi nghĩ rằng điều này defer ws.Done() nên là tuyên bố đầu tiên trong chức năng của bạn.

Tôi thích sự đơn giản của WaitGroup. Tuy nhiên, tôi không thích rằng chúng ta cần phải chuyển tham chiếu đến goroutine vì điều đó có nghĩa là logic đồng thời sẽ được trộn lẫn với logic nghiệp vụ của bạn.

Vì vậy, tôi đã đưa ra chức năng chung này để giải quyết vấn đề này cho tôi:

// Parallelize parallelizes the function calls 
func Parallelize(functions ...func()) { 
    var waitGroup sync.WaitGroup 
    waitGroup.Add(len(functions)) 

    defer waitGroup.Wait() 

    for _, function := range functions { 
     go func(copy func()) { 
      defer waitGroup.Done() 
      copy() 
     }(function) 
    } 
} 

Vì vậy, ví dụ bạn có thể được giải quyết theo cách này:

type Object struct { 
    //data 
} 

func (obj *Object) Update() { 
    //update data 
    time.Sleep(time.Second) 
    fmt.Println("Update done") 
    return 
} 

func main() { 
    functions := []func(){} 
    list := make([]Object, 5) 
    for _, object := range list { 
     function := func(obj Object){ object.Update() }(object) 
     functions = append(functions, function) 
    } 

    Parallelize(functions...)   

    fmt.Println("Group done") 
} 

Nếu bạn muốn sử dụng nó, bạn có thể tìm thấy nó ở đây https://github.com/shomali11/util

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