Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I atomically increment a variable in Swift?

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:

  • Are you using GCD? No. I am not using GCD. Having to use a queue system to increment a number seems overkill.
  • Do You understand basic thread safety? Yes I do otherwise I would not be asking about atomic increments.
  • Is this variable local? No.
  • Is it instance level? Yes it should be part of a single instance.

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()       }  } 
like image 571
fabrizioM Avatar asked Jun 15 '15 17:06

fabrizioM


People also ask

Can you use ++ in Swift?

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.

How do I make a Swift variable safe thread?

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.

Are Swift variables atomic?

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.


1 Answers

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.

like image 75
Martin R Avatar answered Sep 19 '22 05:09

Martin R