Hone logo
Hone
Problems

Concurrent Data Access and Conflict Resolution in Go

This challenge focuses on implementing a robust mechanism for handling concurrent access to shared data in Go. Simulating a scenario where multiple goroutines attempt to modify the same data structure simultaneously, you'll need to design and implement a conflict resolution strategy to ensure data integrity and prevent race conditions. This is a fundamental skill for building reliable concurrent applications.

Problem Description

You are tasked with creating a system that manages a shared bank account balance. Multiple goroutines will concurrently attempt to deposit and withdraw funds from this account. The core requirement is to prevent race conditions and ensure that the balance remains consistent, even under heavy concurrent load.

You must implement the following:

  1. BankAccount struct: This struct should contain a single field: balance (an int).
  2. Deposit(amount int) method: This method should atomically increment the balance by the given amount.
  3. Withdraw(amount int) method: This method should atomically decrement the balance by the given amount only if the current balance is greater than or equal to the amount. If the balance is insufficient, the withdrawal should be rejected, and the balance should remain unchanged.
  4. Balance() method: This method should return the current balance.
  5. Conflict Resolution: Use a sync.Mutex to protect the balance field from concurrent access. Ensure that all operations on the balance are performed under the protection of this mutex.
  6. Error Handling: The Withdraw method should return a boolean value indicating whether the withdrawal was successful. true means the withdrawal succeeded, false means it failed (insufficient funds).

Examples

Example 1:

Input: Initial balance = 100, Goroutine 1 deposits 50, Goroutine 2 withdraws 25.
Output: Final balance = 125, Withdraw successful.
Explanation: Goroutine 1 deposits 50, increasing the balance to 150. Goroutine 2 then withdraws 25, resulting in a final balance of 125. The withdrawal is successful.

Example 2:

Input: Initial balance = 50, Goroutine 1 withdraws 75.
Output: Final balance = 50, Withdraw failed.
Explanation: Goroutine 1 attempts to withdraw 75 from a balance of 50. Since the balance is insufficient, the withdrawal fails, and the balance remains at 50. The function returns false.

Example 3: (Edge Case - Concurrent Withdrawals)

Input: Initial balance = 100, Goroutine 1 withdraws 50, Goroutine 2 withdraws 60 simultaneously.
Output: Final balance = -10 (or 100 depending on the order of execution, but *consistent*), Withdraw 1 successful, Withdraw 2 failed.
Explanation:  The mutex ensures that only one withdrawal can happen at a time.  If Goroutine 1 withdraws 50 first, the balance becomes 50. Then, Goroutine 2 attempts to withdraw 60, but fails because the balance is insufficient. The final balance is 50. If Goroutine 2 withdraws 60 first, the balance becomes 40. Then Goroutine 1 withdraws 50, which fails. The final balance is 40. The important thing is that the operations are atomic and consistent, even with concurrency.

Constraints

  • The initial balance can be any non-negative integer.
  • Deposit amounts will always be non-negative integers.
  • Withdrawal amounts will always be non-negative integers.
  • The number of concurrent goroutines accessing the BankAccount can be significant (e.g., 100+).
  • The code should be efficient and avoid unnecessary locking.
  • The Withdraw method must return a boolean indicating success or failure.

Notes

  • The sync.Mutex is the primary tool for ensuring mutual exclusion.
  • Consider the order of operations when designing your solution. The mutex should protect all access to the balance field.
  • Think about how to handle potential deadlocks (although this is less likely with a single mutex).
  • Focus on correctness and thread safety. Performance is secondary, but avoid obviously inefficient code.
  • Test your solution thoroughly with multiple concurrent goroutines to ensure it handles conflicts correctly. Consider using sync.WaitGroup to wait for all goroutines to complete.
Loading editor...
go