diff --git a/examples.txt b/examples.txt index 8859292..ddace2d 100644 --- a/examples.txt +++ b/examples.txt @@ -32,6 +32,7 @@ Range over Channels Timers Tickers Worker Pools +WaitGroups Rate Limiting Atomic Counters Mutexes diff --git a/examples/channel-synchronization/channel-synchronization.go b/examples/channel-synchronization/channel-synchronization.go index 4cc1461..66873a8 100644 --- a/examples/channel-synchronization/channel-synchronization.go +++ b/examples/channel-synchronization/channel-synchronization.go @@ -1,6 +1,8 @@ // We can use channels to synchronize execution // across goroutines. Here's an example of using a // blocking receive to wait for a goroutine to finish. +// When waiting for multiple goroutines to finish, +// you may prefer to use a [WaitGroup](waitgroups). package main diff --git a/examples/channel-synchronization/channel-synchronization.hash b/examples/channel-synchronization/channel-synchronization.hash index 8680448..e906123 100644 --- a/examples/channel-synchronization/channel-synchronization.hash +++ b/examples/channel-synchronization/channel-synchronization.hash @@ -1,2 +1,2 @@ -fe3e2ea1a67d0f95ce4cb18f3e8aa16d416de0ce -0DfW-1RMqi +eb022977181884c2ab0f2b69e50311769e67a509 +8lmP8beav0p diff --git a/examples/waitgroups/waitgroups.go b/examples/waitgroups/waitgroups.go new file mode 100644 index 0000000..dc8d0c1 --- /dev/null +++ b/examples/waitgroups/waitgroups.go @@ -0,0 +1,42 @@ +// To wait for multiple goroutines to finish, we can +// use a *wait group*. + +package main + +import ( + "fmt" + "sync" + "time" +) + +// This is the function we'll run in every goroutine. +// Note that a WaitGroup must be passed to functions by +// pointer. +func worker(id int, wg *sync.WaitGroup) { + fmt.Printf("Worker %d starting\n", id) + + // Sleep to simulate an expensive task. + time.Sleep(time.Second) + fmt.Printf("Worker %d done\n", id) + + // Notify the WaitGroup that this worker is done. + wg.Done() +} + +func main() { + + // This WaitGroup is used to wait for all the + // goroutines launched here to finish. + var wg sync.WaitGroup + + // Launch several goroutines and increment the WaitGroup + // counter for each. + for i := 1; i <= 5; i++ { + wg.Add(1) + go worker(i, &wg) + } + + // Block until the WaitGroup counter goes back to 0; + // all the workers notified they're done. + wg.Wait() +} diff --git a/examples/waitgroups/waitgroups.hash b/examples/waitgroups/waitgroups.hash new file mode 100644 index 0000000..71ecfc8 --- /dev/null +++ b/examples/waitgroups/waitgroups.hash @@ -0,0 +1,2 @@ +499c7ee59b2ae06d2d3171768d9cf11762121a87 +gLLmgcR7YkP diff --git a/examples/waitgroups/waitgroups.sh b/examples/waitgroups/waitgroups.sh new file mode 100644 index 0000000..c2a6c11 --- /dev/null +++ b/examples/waitgroups/waitgroups.sh @@ -0,0 +1,14 @@ +$ go run waitgroups.go +Worker 5 starting +Worker 3 starting +Worker 4 starting +Worker 1 starting +Worker 2 starting +Worker 4 done +Worker 1 done +Worker 2 done +Worker 5 done +Worker 3 done + +# The order of workers starting up and finishing +# is likely to be different for each invocation. diff --git a/examples/worker-pools/worker-pools.go b/examples/worker-pools/worker-pools.go index 1114a22..eabda7a 100644 --- a/examples/worker-pools/worker-pools.go +++ b/examples/worker-pools/worker-pools.go @@ -42,6 +42,9 @@ func main() { close(jobs) // Finally we collect all the results of the work. + // This also ensures that the worker goroutines have + // finished. An alternative way to wait for multiple + // goroutines is to use a [WaitGroup](waitgroups). for a := 1; a <= 5; a++ { <-results } diff --git a/examples/worker-pools/worker-pools.hash b/examples/worker-pools/worker-pools.hash index 013b2de..9f101b7 100644 --- a/examples/worker-pools/worker-pools.hash +++ b/examples/worker-pools/worker-pools.hash @@ -1,2 +1,2 @@ -1f9acf1e50be05cad73e6b085ed3294892c67d42 -RTRcHA05vV +bc69c6602d438413dcb9ceac112299ee253e4575 +yuHsGf712D1 diff --git a/public/channel-synchronization b/public/channel-synchronization index 7a974b3..307251a 100644 --- a/public/channel-synchronization +++ b/public/channel-synchronization @@ -27,7 +27,9 @@

We can use channels to synchronize execution across goroutines. Here’s an example of using a -blocking receive to wait for a goroutine to finish.

+blocking receive to wait for a goroutine to finish. +When waiting for multiple goroutines to finish, +you may prefer to use a WaitGroup.

@@ -41,7 +43,7 @@ blocking receive to wait for a goroutine to finish.

- +
package main
 
diff --git a/public/index.html b/public/index.html index eaffb11..347a8a3 100644 --- a/public/index.html +++ b/public/index.html @@ -103,6 +103,8 @@
  • Worker Pools
  • +
  • WaitGroups
  • +
  • Rate Limiting
  • Atomic Counters
  • diff --git a/public/waitgroups b/public/waitgroups new file mode 100644 index 0000000..7648b36 --- /dev/null +++ b/public/waitgroups @@ -0,0 +1,218 @@ + + + + + Go by Example: WaitGroups + + + + +
    +

    Go by Example: WaitGroups

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +

    To wait for multiple goroutines to finish, we can +use a wait group.

    + +
    + + +
    + + + +
    package main
    +
    + +
    + + + +
    import (
    +    "fmt"
    +    "sync"
    +    "time"
    +)
    +
    + +
    +

    This is the function we’ll run in every goroutine. +Note that a WaitGroup must be passed to functions by +pointer.

    + +
    + +
    func worker(id int, wg *sync.WaitGroup) {
    +    fmt.Printf("Worker %d starting\n", id)
    +
    + +
    +

    Sleep to simulate an expensive task.

    + +
    + +
        time.Sleep(time.Second)
    +    fmt.Printf("Worker %d done\n", id)
    +
    + +
    +

    Notify the WaitGroup that this worker is done.

    + +
    + +
        wg.Done()
    +}
    +
    + +
    + + + +
    func main() {
    +
    + +
    +

    This WaitGroup is used to wait for all the +goroutines launched here to finish.

    + +
    + +
        var wg sync.WaitGroup
    +
    + +
    +

    Launch several goroutines and increment the WaitGroup +counter for each.

    + +
    + +
        for i := 1; i <= 5; i++ {
    +        wg.Add(1)
    +        go worker(i, &wg)
    +    }
    +
    + +
    +

    Block until the WaitGroup counter goes back to 0; +all the workers notified they’re done.

    + +
    + +
        wg.Wait()
    +}
    +
    + +
    + + + + + + + + + + + + + +
    + + + +
    $ go run waitgroups.go
    +Worker 5 starting
    +Worker 3 starting
    +Worker 4 starting
    +Worker 1 starting
    +Worker 2 starting
    +Worker 4 done
    +Worker 1 done
    +Worker 2 done
    +Worker 5 done
    +Worker 3 done
    +
    + +
    +

    The order of workers starting up and finishing +is likely to be different for each invocation.

    + +
    + + +
    + + +

    + Next example: Rate Limiting. +

    + + +
    + + diff --git a/public/worker-pools b/public/worker-pools index baeb60a..5762746 100644 --- a/public/worker-pools +++ b/public/worker-pools @@ -40,7 +40,7 @@ a worker pool using goroutines and channels.

    - +
    package main
     
    @@ -147,7 +147,10 @@ channel to indicate that’s all the work we have.

    -

    Finally we collect all the results of the work.

    +

    Finally we collect all the results of the work. +This also ensures that the worker goroutines have +finished. An alternative way to wait for multiple +goroutines is to use a WaitGroup.

    @@ -207,7 +210,7 @@ there are 3 workers operating concurrently.

    - Next example: Rate Limiting. + Next example: WaitGroups.