Waitgroups in Groovy

Our example demonstrates how to wait for multiple threads to finish using a CountDownLatch. This is similar to the concept of WaitGroups in other languages.

import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executors

// This is the function we'll run in every thread.
def worker(int id, CountDownLatch latch) {
    println "Worker ${id} starting"
    
    // Sleep to simulate an expensive task.
    Thread.sleep(1000)
    println "Worker ${id} done"
    
    // Signal that this worker is done
    latch.countDown()
}

// Main function
def main() {
    // This CountDownLatch is used to wait for all the
    // threads launched here to finish.
    def numWorkers = 5
    def latch = new CountDownLatch(numWorkers)
    
    // Create a fixed thread pool
    def executor = Executors.newFixedThreadPool(numWorkers)
    
    // Launch several threads and decrement the CountDownLatch
    // counter for each.
    (1..numWorkers).each { i ->
        executor.execute {
            worker(i, latch)
        }
    }
    
    // Block until the CountDownLatch counter goes back to 0;
    // all the workers notified they're done.
    latch.await()
    
    // Shutdown the executor
    executor.shutdown()
    
    // Note that this approach has no straightforward way
    // to propagate errors from workers. For more
    // advanced use cases, consider using other concurrency 
    // utilities provided by Java/Groovy.
}

// Run the main function
main()

To run the program, save it as waitgroups.groovy and use the groovy command:

$ groovy waitgroups.groovy
Worker 1 starting
Worker 2 starting
Worker 3 starting
Worker 4 starting
Worker 5 starting
Worker 1 done
Worker 2 done
Worker 3 done
Worker 4 done
Worker 5 done

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

In this Groovy example, we’ve used a CountDownLatch to replicate the functionality of a WaitGroup. The CountDownLatch is initialized with the number of workers, and each worker calls countDown() when it’s finished. The main thread waits for all workers to finish by calling await() on the latch.

We’ve also used a FixedThreadPool to manage our threads, which is a common practice in Java/Groovy for handling multiple concurrent tasks.

Remember that while this example demonstrates basic concurrency, Groovy and Java offer more advanced concurrency utilities for complex scenarios.