I want to be able to increment a counter atomically and I can't find any reference on how to do it.
Adding more information based on comments:
I want to do something like this:
class Counter { private var mux Mutex private (set) value Int func increment (){ mux.lock() value += 1 mux.unlock() } }
Increment and Decrement operatorSwift provides an increment operator ++ and a decrement operator to increase or decrease the value of a numeric variable by 1. The operator with variables of any integer or floating-point type is used. The ++ and -- symbol is used as a prefix operator or postfix operator.
In Swift, any variable declared with the let keyword is a constant and, therefore, read-only and thread-safe. When a variable is declared as var it becomes mutable and not thread-safe unless the data type is specifically designed to be thread-safe when mutable.
Swift, queues, processes, power, threads, variables, all don't matter. An atomic operation is an operation that is not divisible into any other, smaller operations.
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 choose the 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
and friends 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 best solution 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 thread What advantage(s) does dispatch_sync have over @synchronized? is also very interesting.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With