Atomic Counters in Objective-C
Atomic counters are useful for managing state across multiple threads. In this example, we’ll look at using atomic operations to safely increment a counter accessed by multiple threads.
#import <Foundation/Foundation.h>
#import <libkern/OSAtomic.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
// We'll use an atomic 64-bit integer to represent our counter
__block int64_t ops = 0;
// We'll use a dispatch group to wait for all threads to finish
dispatch_group_t group = dispatch_group_create();
// We'll start 50 threads that each increment the counter exactly 1000 times
for (int i = 0; i < 50; i++) {
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int c = 0; c < 1000; c++) {
// To atomically increment the counter we use OSAtomicIncrement64
OSAtomicIncrement64(&ops);
}
});
}
// Wait until all the threads are done
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
// Here no threads are writing to 'ops', but it's safe to read the value
NSLog(@"ops: %lld", ops);
}
return 0;
}
In this Objective-C version:
We use
int64_t
as our counter type, which is a 64-bit integer.Instead of Go’s WaitGroup, we use a Grand Central Dispatch (GCD) group (
dispatch_group_t
) to wait for all threads to complete.We replace Go’s goroutines with GCD’s
dispatch_group_async
, which allows us to dispatch blocks of code to be executed concurrently.For atomic incrementation, we use
OSAtomicIncrement64
from thelibkern/OSAtomic.h
header. This function atomically increments the 64-bit integer.We use
NSLog
for output instead of Go’sfmt.Println
.
When you run this program, you should see:
ops: 50000
We expect to get exactly 50,000 operations. If we had used a non-atomic integer and incremented it with regular addition, we’d likely get a different number, changing between runs, because the threads would interfere with each other.
Note that in more recent versions of iOS and macOS, Apple recommends using stdatomic.h
for atomic operations. However, OSAtomic
functions are still widely used and are more similar to the Go example in terms of API.
Next, we could look at other synchronization primitives in Objective-C, such as locks or semaphores, for managing state across threads.