package main import ( "fmt" "sync" "time" ) // Resource represents some shared resource that needs controlled access. type Resource struct { name string } func (r *Resource) Use() { fmt.Printf(" Using resource %s... ", r.name) time.Sleep(200 * time.Millisecond) // Simulate work with the resource fmt.Printf(" Finished using resource %s. ", r.name) } // Worker simulates a goroutine that needs to access the resource. func Worker(id int, semaphore chan struct{}, resource *Resource, wg *sync.WaitGroup) { defer wg.Done() fmt.Printf("Worker %d: Waiting for semaphore... ", id) // Acquire a token from the semaphore (blocking operation) semaphore <- struct{}{} fmt.Printf("Worker %d: Acquired semaphore, accessing resource. ", id) resource.Use() // Release the token back to the semaphore <-semaphore fmt.Printf("Worker %d: Released semaphore. ", id) } func main() { maxConcurrent := 2 // Max number of goroutines that can access the resource concurrently // Create a buffered channel to act as a semaphore. // Its capacity limits the number of concurrent "tokens". semaphore := make(chan struct{}, maxConcurrent) sharedResource := &Resource{name: "DatabaseConnectionPool"} var wg sync.WaitGroup numWorkers := 5 fmt.Printf("Main: Launching %d workers, max %d concurrent resource users. ", numWorkers, maxConcurrent) for i := 0; i < numWorkers; i++ { wg.Add(1) go Worker(i+1, semaphore, sharedResource, &wg) time.Sleep(50 * time.Millisecond) // Stagger worker starts slightly } wg.Wait() fmt.Println(" Main: All workers finished. Semaphore demonstration complete.") }