2015-09-29 36 views
7

Cách thức thành ngữ để gán thời gian chờ là WaitGroup.Wait() là gì?Thời gian chờ cho WaitGroup.Wait()

Lý do tôi muốn làm điều này, là để bảo vệ 'bộ lập lịch' của tôi khỏi khả năng đang chờ một 'công nhân' sai lệch bao giờ hết. Điều này dẫn đến một số câu hỏi triết học (tức là làm thế nào hệ thống có thể tiếp tục một cách đáng tin cậy khi nó có công nhân sai lầm?), Nhưng tôi nghĩ điều đó nằm ngoài phạm vi của câu hỏi này.

Tôi có câu trả lời mà tôi sẽ cung cấp. Bây giờ tôi đã viết nó xuống, nó không có vẻ xấu như vậy nhưng nó vẫn cảm thấy phức tạp hơn nó nên. Tôi muốn biết nếu có một cái gì đó có sẵn mà là đơn giản hơn, thành ngữ hơn, hoặc thậm chí là một cách tiếp cận thay thế mà không sử dụng WaitGroups.

Ta.

Trả lời

15

Chủ yếu là giải pháp bạn đã đăng below là tốt như nó có thể nhận được. Một số mẹo để cải thiện:

  • Hoặc bạn có thể đóng kênh để hoàn thành tín hiệu thay vì gửi giá trị lên kênh, thao tác nhận trên kênh đóng can always proceed immediately.
  • Và tốt hơn hết là sử dụng câu lệnh defer để hoàn thành tín hiệu, nó được thực thi ngay cả khi chức năng chấm dứt đột ngột.
  • Ngoài ra nếu chỉ có một "công việc" để chờ, bạn hoàn toàn có thể bỏ qua WaitGroup và chỉ gửi giá trị hoặc đóng kênh khi hoàn thành công việc (cùng một kênh bạn sử dụng trong tuyên bố select).
  • Chỉ định thời lượng 1 giây đơn giản như: timeout := time.Second. Chỉ định 2 giây, ví dụ: timeout := 2 * time.Second. Bạn không cần chuyển đổi, time.Second đã thuộc loại time.Duration, nhân nó với một hằng số chưa được nhập như 2 cũng sẽ mang lại giá trị loại time.Duration.

Tôi cũng sẽ tạo một chức năng trợ giúp/tiện ích bao gồm chức năng này. Lưu ý rằng WaitGroup phải được chuyển dưới dạng con trỏ khác, bản sao sẽ không nhận được "thông báo" của các cuộc gọi WaitGroup.Done(). Một cái gì đó như:

// waitTimeout waits for the waitgroup for the specified max timeout. 
// Returns true if waiting timed out. 
func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool { 
    c := make(chan struct{}) 
    go func() { 
     defer close(c) 
     wg.Wait() 
    }() 
    select { 
    case <-c: 
     return false // completed normally 
    case <-time.After(timeout): 
     return true // timed out 
    } 
} 

Sử dụng nó:

if waitTimeout(&wg, time.Second) { 
    fmt.Println("Timed out waiting for wait group") 
} else { 
    fmt.Println("Wait group finished") 
} 

Hãy thử nó trên Go Playground.

+0

cảm ơn, câu trả lời rất kỹ lưỡng. Cuối cùng tôi đã ngừng sử dụng một WaitGroup hoàn toàn, và sử dụng một kênh duy nhất để thay thế. Mã của tôi kết thúc đơn giản hơn, rõ ràng hơn và 'ít có vấn đề' hơn. Mã của tôi có thể đếm số lần lặp lại, vì vậy tôi có thể sử dụng lệnh chọn một số lần cố định, sau đó sử dụng một NewTimer duy nhất cho thời gian chờ. Để hoàn thành, tôi sẽ thêm một câu trả lời khác trong khóa học. – laher

+1

Vì vậy, một chút FYI vì điều này chỉ xuất hiện trong # go-nut: Điều này sẽ * không * hoạt động nếu các cuộc gọi 'wg.Add' là * bên trong một goroutine *. Chỉ cần bình luận trong trường hợp ai đó sử dụng mã này theo cách tương tự với vấn đề # go-nut vừa giải quyết! –

+1

nếu 'wg.Wait()' không bao giờ trở lại, nó có rò rỉ một goroutine không? – coanor

4

tôi đã làm nó như thế này: http://play.golang.org/p/eWv0fRlLEC

go func() { 
    wg.Wait() 
    c <- struct{}{} 
}() 
timeout := time.Duration(1) * time.Second 
fmt.Printf("Wait for waitgroup (up to %s)\n", timeout) 
select { 
case <-c: 
    fmt.Printf("Wait group finished\n") 
case <-time.After(timeout): 
    fmt.Printf("Timed out waiting for wait group\n") 
} 
fmt.Printf("Free at last\n") 

Nó hoạt động tốt, nhưng nó cách tốt nhất để làm điều đó?

0

Tôi đã viết thư viện đóng gói logic đồng thời https://github.com/shomali11/parallelizer mà bạn cũng có thể vượt qua thời gian chờ.

Dưới đây là một ví dụ mà không có một thời gian chờ:

func main() { 
    group := parallelizer.DefaultGroup() 

    group.Add(func() { 
     for char := 'a'; char < 'a'+3; char++ { 
      fmt.Printf("%c ", char) 
     } 
    }) 

    group.Add(func() { 
     for number := 1; number < 4; number++ { 
      fmt.Printf("%d ", number) 
     } 
    }) 

    err := group.Run() 

    fmt.Println() 
    fmt.Println("Done") 
    fmt.Printf("Error: %v", err) 
} 

Output:

a 1 b 2 c 3 
Done 
Error: <nil> 

Dưới đây là một ví dụ với một thời gian chờ:

func main() { 
    options := &parallelizer.Options{Timeout: time.Second} 
    group := parallelizer.NewGroup(options) 

    group.Add(func() { 
     time.Sleep(time.Minute) 

     for char := 'a'; char < 'a'+3; char++ { 
      fmt.Printf("%c ", char) 
     } 
    }) 

    group.Add(func() { 
     time.Sleep(time.Minute) 

     for number := 1; number < 4; number++ { 
      fmt.Printf("%d ", number) 
     } 
    }) 

    err := group.Run() 

    fmt.Println() 
    fmt.Println("Done") 
    fmt.Printf("Error: %v", err) 
} 

Output:

Done 
Error: timeout 
0

Đây không phải là câu trả lời thực sự cho câu hỏi này nhưng là giải pháp (đơn giản hơn nhiều) đối với vấn đề nhỏ của tôi khi tôi có câu hỏi này.

'Công nhân' của tôi đã thực hiện các yêu cầu http.Get() vì vậy tôi chỉ đặt thời gian chờ trên ứng dụng khách http.

urls := []string{"http://1.jpg", "http://2.jpg"} 
wg := &sync.WaitGroup{} 
for _, url := range urls { 
    wg.Add(1) 
    go func(url string) { 
     client := http.Client{ 
      Timeout: time.Duration(3 * time.Second), // only want very fast responses 
     } 
     resp, err := client.Get(url) 
     //... check for errors 
     //... do something with the image when there are no errors 
     //... 

     wg.Done() 
    }(url) 

} 
wg.Wait() 
Các vấn đề liên quan