How do I atomically increment a variable in Swift? How do I atomically increment a variable in Swift? swift swift

How do I atomically increment a variable in Swift?


From Low-Level Concurrency APIs:

There’s a long list of OSAtomicIncrement and OSAtomicDecrement functions that allow you to increment and decrement an integer value in an atomic way – thread safe without having to take a lock (or use queues). These can be useful if you need to increment global counters from multiple threads for statistics. If all you do is increment a global counter, the barrier-free OSAtomicIncrement versions are fine, and when there’s no contention, they’re cheap to call.

These functions work with fixed-size integers, you can choosethe 32-bit or 64-bit variant depending on your needs:

class Counter {    private (set) var value : Int32 = 0    func increment () {        OSAtomicIncrement32(&value)    }}

(Note: As Erik Aigner correctly noticed, OSAtomicIncrement32 andfriends are deprecated as of macOS 10.12/iOS 10.10. Xcode 8 suggests to use functions from <stdatomic.h> instead. However that seems to be difficult,compare Swift 3: atomic_compare_exchange_strong and https://openradar.appspot.com/27161329. Therefore the following GCD-based approach seems to be the bestsolution now.)

Alternatively, one can use a GCD queue for synchronization.From Dispatch Queues in the "Concurrency Programming Guide":

... With dispatch queues, you could add both tasks to a serial dispatch queue to ensure that only one task modified the resource at any given time. This type of queue-based synchronization is more efficient than locks because locks always require an expensive kernel trap in both the contested and uncontested cases, whereas a dispatch queue works primarily in your application’s process space and only calls down to the kernel when absolutely necessary.

In your case that would be

// Swift 2:class Counter {    private var queue = dispatch_queue_create("your.queue.identifier", DISPATCH_QUEUE_SERIAL)    private (set) var value: Int = 0    func increment() {        dispatch_sync(queue) {            value += 1        }    }}// Swift 3:class Counter {    private var queue = DispatchQueue(label: "your.queue.identifier")     private (set) var value: Int = 0    func increment() {        queue.sync {            value += 1        }    }}

See Adding items to Swift array across multiple threads causing issues (because arrays aren't thread safe) - how do I get around that? or GCD with static functions of a struct for more sophisticated examples. This threadWhat advantage(s) does dispatch_sync have over @synchronized? is also very interesting.


Queues are an overkill in this case. You can use a DispatchSemaphore introduced in Swift 3 for this purpose like so:

import Foundationpublic class AtomicInteger {    private let lock = DispatchSemaphore(value: 1)    private var value = 0    // You need to lock on the value when reading it too since    // there are no volatile variables in Swift as of today.    public func get() -> Int {        lock.wait()        defer { lock.signal() }        return value    }    public func set(_ newValue: Int) {        lock.wait()        defer { lock.signal() }        value = newValue    }    public func incrementAndGet() -> Int {        lock.wait()        defer { lock.signal() }        value += 1        return value    }}

The latest version of the class is available over here.


I know this question is already a little bit older, but I just recently stumbled upon the same problem.After researching a little and reading posts like http://www.cocoawithlove.com/blog/2016/06/02/threads-and-mutexes.html I came up with this solution for an atomic counter. Maybe it will also help others.

import Foundationclass AtomicCounter {  private var mutex = pthread_mutex_t()  private var counter: UInt = 0  init() {    pthread_mutex_init(&mutex, nil)  }  deinit {    pthread_mutex_destroy(&mutex)  }  func incrementAndGet() -> UInt {    pthread_mutex_lock(&mutex)    defer {      pthread_mutex_unlock(&mutex)    }    counter += 1    return counter  }}