Multiple Wait Groups in GO

  • The program goroutine3.go illustrates how we can use different WaitGroups for groups of functions.
  • We use numEvenFuncs for the even-numbered goroutines and numOddFuncs for the odd-numbered goroutines.
/* goroutine3.go
   concurrent functions with even-odd sync */
   
package main
import ("fmt"; "sync"; "math/rand"; "os"; "strconv")

func myFunction(id int, numEvenFuncs, numOddFuncs *sync.WaitGroup) {
   fmt.Printf("\nmyFunction %2d:",id)
   var sum float64 
   for i := 0; i < 10000; i++ {
      sum+=rand.Float64()
   }
   switch id%2{
      case 0: numEvenFuncs.Done()
      case 1: numOddFuncs.Done()
   }
   fmt.Printf("\ndone %7.3e id %2d",sum,id)
}

func main() {
   if len(os.Args) < 2 {
      fmt.Printf("usage <filename> seed\n")
      return
   }
   seed, _ := strconv.Atoi(os.Args[1])
   rand.Seed(int64(seed))
   var numEvenFuncs, numOddFuncs sync.WaitGroup
   fmt.Printf("\n--start main program--")
   numEvenFuncs.Add(10)
   numOddFuncs.Add(10)
   for i := 0; i < 20; i++ {
      go myFunction(i, &numEvenFuncs, &numOddFuncs)
   }
   numEvenFuncs.Wait()
   fmt.Printf("\n--end EVEN goroutines--\n")
   numOddFuncs.Wait()
   fmt.Printf("\n--end ODD goroutines--\n")
   fmt.Printf("\n--end main program--\n")
}
Two different classes of goroutines are synchronized correctly using two waitgroups.

It is instructive to run this program repeatedly and examine the output.

Here is an interesting example:

$ go run goroutine3.go 81

--start main program--
myFunction 19:
done 5.030e+03 id 19
myFunction  0:
done 4.982e+03 id  0
myFunction  1:
done 5.027e+03 id  1
myFunction  2:
done 4.967e+03 id  2
myFunction  3:
done 5.013e+03 id  3
myFunction  4:
done 4.999e+03 id  4
myFunction  5:
done 4.944e+03 id  5
myFunction 12:
myFunction  9:
myFunction 10:
done 4.999e+03 id 10
myFunction 11:
myFunction  7:
myFunction  6:
myFunction 15:
myFunction 17:
myFunction 13:
myFunction 16:
myFunction 14:
myFunction 18:
myFunction  8:
done 5.018e+03 id  9
done 5.027e+03 id 16
done 5.000e+03 id 17
done 4.985e+03 id 14
done 5.006e+03 id 12
done 5.027e+03 id 11
done 5.058e+03 id  6
done 5.014e+03 id  7
done 5.034e+03 id 15
done 4.965e+03 id 18
done 5.049e+03 id  8
--end EVEN goroutines--

done 5.039e+03 id 13
--end ODD goroutines--

--end main program--
  • We notice that 10 each of even and odd functions are launched.
  • They are not launched in a sequential order. This is a property of concurrent and parallel programs.
  • 10 each of even and odd functions are concluded. They do not conclude in the order in which they were launched (again caused by concurrent or parallel execution).
  • One odd goroutine is still active after all the even goroutines have finished.
  • The program runs correctly: 10 each of the even and odd routines are launched and conclude, before the main program concludes.
  • You can experiment with this further by
    • Changing the seed.
    • Changing the number of functions.
    • Putting a separate load on your machine (e.g. by running a separate resource-consuming program in a separate window).