Luca Angeletti
Luca Angeletti

Reputation: 59506

Concurrency and lock in Swift

Premise

We all know there are good numbers and bad numbers in the universe.

I have the following synchronous function

func isGood(number:Int) -> Bool {
    // synchronous connection
    // ...
    // returns a Bool
}

Of course I am not providing the secret implementation here, but you should know that it performs a synchronous internet connection and it does return

The problem

Now, given the 100 integers from 0 to 99, I want to know if at least 51 of them are good numbers.

The synchronous approach

I could write something like this.

func majorityIsGood() -> Bool {
    var count = 0
    for i in 0...99 {
        if isGood(i) {
            count++
            if count > 50 {
                return true
            }
        }
    }
    return false
}

But performing 100 (in the worst case scenario) synchronous call to isGood will require too much time. I need the answer as fast as possible.

The asynchronous approach

I would prefer something like this

func majorityIsGood(completion:(good:Bool) -> ()) {
    var goodNums = 0
    var badNums = 0
    var resultFound = false

    for i in 0...99 {
        dispatch_async(DISPATCH_QUEUE_CONCURRENT) {

            let isGood = isGood(i)

            // lineA
            // begin lock on (resultFound)
            if !resultFound {
                if isGood {
                    goodNums++
                } else {
                    badNums++
                }
                if goodNums > 50 || badNums >= 50 {
                    resultFound = true
                    completion(good: goodNums > 50)
                }
            }
            // end lock on (resultFound)
            // lineB
        }
    }
}

Questions

  1. How can I guarantee a synchronous access to the block of code between lineA and lineB in Swift?
  2. And finally, once I get the result, is it possible to kill the concurrent closures that are still being processed?

Thank you in advance.

Upvotes: 3

Views: 448

Answers (2)

nRewik
nRewik

Reputation: 9148

  1. serial queue can be used to synchronize access to a specific resource.
  2. I'm not sure. If there is a way to kill the concurrent operation that being dispatched. However, if you just want to stop them. take a look at NSOperation cancellation.

Here's the code

func majorityIsGood( completion: ((good:Bool) -> Void) ) {

    var goodNums = 0
    var badNums = 0
    var resultFound = false

    let serialQueue = dispatch_queue_create("com.unique.myQueue", DISPATCH_QUEUE_SERIAL)

    for i in 0...99 {
        dispatch_async(DISPATCH_QUEUE_CONCURRENT) {

            let _isGood = isGood(i)

            // lineA
            dispatch_async(serialQueue){
                if !resultFound {
                    if _isGood {
                        goodNums++
                    } else {
                        badNums++
                    }
                    if goodNums > 50 || badNums >= 50 {
                        resultFound = true
                        completion(good: goodNums > 50)
                    }
                }
            }
            // lineB
        }
    }
}

Upvotes: 2

Rupert
Rupert

Reputation: 2159

Build on the shoulders of giants!

  1. I would inspire myself with https://github.com/ReactiveCocoa/ReactiveCocoa/blob/v3.0-RC.1/ReactiveCocoa/Swift/Atomic.swift and use their lock/unlock mechanism.
  2. As far as I know there is no way to 'kill' closures — that sounds like it could be a case for using an array of NSOperations which provide a cancellation mechanism (although it should be noted that it is actually up to you to check the isCancelled flag at appropriate points in your NSOperation so that you can stop whatever work it is doing.

Upvotes: 1

Related Questions