package main import ( "fmt" "sync" "time" ) func worker(id int, readyToProceed chan<- struct{}, startSignal <-chan struct{}) { fmt.Printf("Worker %d: Started initial work. ", id) time.Sleep(time.Duration(id+1) * 100 * time.Millisecond) // Simulate initial work fmt.Printf("Worker %d: Ready to proceed to stage 2. ", id) readyToProceed <- struct{}{} // Signal readiness for stage 2 <-startSignal // Wait for the barrier to lift (channel close or send) fmt.Printf("Worker %d: Proceeding with stage 2. ", id) time.Sleep(time.Duration(id+1) * 50 * time.Millisecond) // Simulate stage 2 work fmt.Printf("Worker %d: Finished stage 2. ", id) } func main() { const numWorkers = 5 const barrierThreshold = 3 // We need 3 workers to be ready to proceed readyToProceed := make(chan struct{}) // Workers signal readiness here startSignal := make(chan struct{}) // Signal for workers to proceed var wg sync.WaitGroup // Start workers for i := 0; i < numWorkers; i++ { wg.Add(1) go func(id int) { defer wg.Done() worker(id, readyToProceed, startSignal) }(i) } // Barrier coordinator go func() { fmt.Printf("Coordinator: Waiting for %d workers to be ready... ", barrierThreshold) readyCount := 0 for range readyToProceed { readyCount++ fmt.Printf("Coordinator: %d workers ready. ", readyCount) if readyCount >= barrierThreshold { fmt.Println("Coordinator: Barrier reached! Signalling workers to proceed. ") close(startSignal) // Close channel to signal all waiting workers break } } // If there are more workers than barrierThreshold, they will still send on readyToProceed // and block. We need to drain it or close it. For this example, we let it run. // In a real application, you might use a buffered channel or a select with a default. // Or ensure 'readyToProceed' is closed when all potential signals are sent. // For simplicity, we assume workers will proceed after the barrier is lifted. }() wg.Wait() // Wait for all workers to finish all their stages fmt.Println("Main: All workers completed.") }