2015-09-29 22 views
7

WaitGroup.Wait() için zaman aşımı atamanın deyimsel yolu nedir?WaitGroup.Wait() için zaman aşımı

Bunu yapmak istememin sebebi, 'zamanlayıcı'mı potansiyel bir bekçinin' işçisini 'beklemek zorunda kalmaktan korumaktır. Bu, bazı felsefi sorulara yol açmaktadır (diğer bir deyişle, çalışanlar işe yaramazsa sistem nasıl güvenilir bir şekilde devam edebilir?), Ama bence bu sorunun kapsamı dışında kalıyor.

Vereceğim bir cevabım var. Şimdi onu yazdım, o kadar da kötü görünmüyor ama yine de olması gerekenden daha kıvrık geliyor. Daha basit, daha akılcı, hatta WaitGroups kullanmayan alternatif bir yaklaşımın mevcut olup olmadığını bilmek isterim.

Ta.

cevap

15

Çoğunlukla below'u gönderdiğiniz çözüm, alabileceği kadar iyidir. Birkaç ipucu onu geliştirmek için:

  • Alternatif bir kapalı kanalda can always proceed immediately üzerine alma işleminin yerine üzerinde bir değere gönderme tamamlandığının belirtilmesi için kanalı kapatabilir.
  • Ayrıca, tamamlanma bittiğinde bile işlev tamamlandığında, defer bildiriminin kullanılması daha iyidir.
  • Ayrıca, beklemek için yalnızca bir "iş" varsa, WaitGroup'u tamamen atlayabilir ve iş tamamlandığında (select ifadenizde kullandığınız aynı kanal) bir değer gönderebilir veya kanalı kapatabilirsiniz.
  • 1 saniye süresinin belirlenmesi şu kadar basittir: timeout := time.Second. Örneğin 2 saniyeyi belirtmek: timeout := 2 * time.Second. Dönüşüme ihtiyacınız yoktur, time.Second zaten time.Duration türünde, 2 gibi bir türlenmemiş sabitle çarparak time.Duration türünde bir değer verir.

Bu işlevi sarmalayan bir yardımcı/yardımcı işlev de oluşturacağım. WaitGroup'un bir işaretçi olarak iletilmesi gerektiğine dikkat edin; kopya, WaitGroup.Done() çağrılarının "bildirim" ini almayacaktır. Bunu kullanarak

// 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 
    } 
} 

: Böyle bir şey

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

o Go Playground üzerinde deneyin.

+0

sayesinde çok kapsamlı bir cevap. Sonunda bir WaitGroup kullanmayı tamamen bıraktım ve bunun yerine tek bir kanal kullandım. Kodum daha basit, daha açık ve 'daha az sorunlu' oldu. Kodum yineleme sayısını sayabilirdi, bu yüzden select deyimini sabit sayıda kullanabilirdim, sonra zaman aşımı için tek bir NewTimer kullanabiliyordum. Tamlık için, zaman içerisinde başka bir cevap ekleyeceğim. – laher

+1

bu sadece # go-fındık de geçiyor Bilginize beri Yani, biraz: Bu irade * değil * çalışma 'wg.Add' aramalar goroutine içine * * Eğer. Sadece birisinin bu kodu sorunla benzer bir şekilde kullanması durumunda yorum yapıyor # go-nuts sadece çözüldü! –

+1

eğer 'wg.Wait()' asla geri, bir goroutine sızan geliyor? 'Wg.Wait()' döndürür hiçbir zaman – coanor

4

böyle yaptım: 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") 

Düzgün çalışır, ancak bunu yapmanın en iyi yolu nedir?

0

Ayrıca zaman aşımı geçirebileceğiniz eşzamanlılık mantığı https://github.com/shomali11/parallelizer'u içine alan bir kitaplık yazdım.İşte

zaman aşımına uğramadan bir örnektir:

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

Çıktı:

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

Çıktı:

Done 
Error: timeout 
İşte
a 1 b 2 c 3 
Done 
Error: <nil> 

zaman aşımı ile bir örnektir 210
0

Bu, bu soruya gerçek bir cevap değil ama bu soru vardı benim küçük bir sorun (daha basit) çözüm oldu.

Benim 'işçi yüzden sadece http istemci üzerinde zaman aşımını ayarlamak http.Get() isteklerini yapıyorlardı.

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()