Hone logo
Hone
Problems

Implementing Acquire-Release Semantics with Mutexes in Go

Concurrency control is fundamental in modern software development. Understanding how to manage shared resources safely between multiple goroutines is crucial. This challenge focuses on implementing acquire-release semantics using Go's built-in sync.Mutex, a common tool for mutual exclusion. You will simulate a scenario where multiple goroutines try to access and modify a shared resource, and you need to ensure data integrity using the acquire-release pattern.

Problem Description

Your task is to implement a mechanism that simulates acquire-release semantics for a shared integer counter using Go's sync.Mutex. Acquire-release semantics guarantee that any writes performed by a goroutine before it releases a lock are visible to any other goroutine that subsequently acquires the same lock.

You will create a SafeCounter struct that encapsulates an integer value and a sync.Mutex. You need to implement two methods: Increment and GetValue.

Key Requirements:

  • Increment(): This method should atomically increment the value by 1. It must acquire the mutex before modifying the value and release it afterward.
  • GetValue(): This method should return the current value of the counter. It must also acquire the mutex before reading the value and release it afterward to ensure it reads the most up-to-date value.

Expected Behavior:

When multiple goroutines concurrently call Increment, the final value of the SafeCounter should be exactly the total number of times Increment was called across all goroutines. This demonstrates that the acquire-release pattern correctly synchronizes access and prevents race conditions.

Edge Cases:

  • Zero calls: If Increment is never called, GetValue should return 0.
  • High concurrency: The solution should behave correctly even when a large number of goroutines are calling Increment simultaneously.

Examples

Example 1:

Input:
Goroutines: 10
Increments per goroutine: 1000

Expected Output:
Final Counter Value: 10000

Explanation: 10 goroutines each increment the counter 1000 times. With proper acquire-release semantics, the final value should be the sum of all increments: 10 * 1000 = 10000.

Example 2:

Input:
Goroutines: 5
Increments per goroutine: 500

Expected Output:
Final Counter Value: 2500

Explanation: 5 goroutines each increment the counter 500 times. The total expected value is 5 * 500 = 2500.

Constraints

  • The SafeCounter struct must contain a sync.Mutex.
  • The Increment and GetValue methods must use the mutex to protect access to the value.
  • The solution should be written in Go.
  • The number of goroutines and increments per goroutine can be up to 1000 each in testing scenarios.

Notes

  • The sync.Mutex in Go provides mutual exclusion. Calling Lock() on a mutex blocks until the mutex is unlocked. Calling Unlock() releases the mutex.
  • Acquire-release semantics are inherently provided by the Lock() and Unlock() operations of a sync.Mutex. Any write that happens between Lock() and Unlock() is guaranteed to be visible to any goroutine that subsequently acquires the lock.
  • Consider using sync.WaitGroup to manage the lifecycle of your goroutines and ensure the main program waits for all increments to complete before checking the final value.
Loading editor...
go