Hone logo
Hone
Problems

Concurrent Counter with Atomic Operations

In concurrent programming, multiple goroutines might attempt to access and modify shared data simultaneously. This can lead to race conditions where the final state of the data is unpredictable and incorrect. This challenge focuses on using Go's sync/atomic package to safely manage shared mutable state in a concurrent environment.

Problem Description

You are tasked with implementing a concurrent counter that can be safely incremented and read by multiple goroutines. The goal is to ensure that the counter's value is always accurate, even under heavy concurrent access, by utilizing atomic operations.

Requirements:

  1. Atomic Increment: Implement a function Increment(counter *int64) that atomically increments the value pointed to by counter. This function should be safe to call from multiple goroutines concurrently without causing race conditions.
  2. Atomic Read: Implement a function GetValue(counter *int64) that atomically reads and returns the current value of the counter pointed to by counter. This read should also be thread-safe.
  3. Initialization: The counter should be initialized to 0.

Expected Behavior:

When multiple goroutines concurrently call Increment, the final value of the counter should accurately reflect the total number of increments performed. Similarly, calling GetValue at any point should return the most up-to-date, consistent value of the counter.

Edge Cases:

  • Handling a very large number of concurrent increments.
  • Ensuring reads are consistent even during increments.

Examples

Example 1:

Input:
- Number of goroutines: 10
- Increments per goroutine: 1000

Output:
The final value of the counter should be 10000.

Explanation: If 10 goroutines each increment the counter 1000 times, the total number of increments should be 10 * 1000 = 10000. The atomic operations ensure that each increment is processed correctly, and the final value reflects this total.

Example 2:

Input:
- Number of goroutines: 5
- Increments per goroutine: 100000
- Intermediate reads: After each goroutine has performed 50000 increments, the value is read.

Output:
The initial intermediate read should be close to 250000 (5 goroutines * 50000 increments).
The final value of the counter should be 500000.

Explanation: This example tests both increment and read operations under concurrency. The atomic read should provide a consistent snapshot of the counter's value, and the final value should again reflect the total increments.

Constraints

  • The counter will be of type int64.
  • The number of goroutines can range from 1 to 1000.
  • The number of increments per goroutine can range from 1 to 1,000,000.
  • The solution must use functions from the sync/atomic package.
  • The solution should be efficient and avoid performance bottlenecks caused by synchronization.

Notes

  • Consider how sync/atomic functions operate on pointers to primitive types.
  • Think about the potential race conditions that would occur if you were to use a simple *counter++ operation without atomic primitives.
  • The sync/atomic package provides functions like AddInt64 for atomic increments and LoadInt64 for atomic reads.
Loading editor...
go