Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

perform simultaneous operations based on the some conditions in swift

I have to perform set of operations based on some conditions like fetching data from database which takes approximately 10 seconds per query (ConnectedDevices.getAllDetails() takes 10 to execute and return result).

this may be similar to below question optimized way to search a device ip address within a range in iphone but in my case i need to perform operations in batches as shown in below code :

var isConditionTrue = false
var numProcessed = 0
let dbQueue = dispatch_queue_create("dbQueue", DISPATCH_QUEUE_SERIAL)

// case 1
    for i in 1...10 {

            dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_UTILITY.value), 0)) {
            let eachDBValue = ConnectedDevices.getAllDetails(i)

                dispatch_async(dbQueue) {
                    if !eachDBValue {
                        numProcessed++
                    }
                }
            }
        }


// case 2
for i in 11...20 {

            dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_UTILITY.value), 0)) {
            let eachDBValue = ConnectedDevices.getAllDetails(i)

                dispatch_async(dbQueue) {
                    if !eachDBValue {
                        numProcessed++
                    }
                }
            }
        }


// case 3
for i in 21...30 {

            dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_UTILITY.value), 0)) {
            let eachDBValue = ConnectedDevices.getAllDetails(i)

                dispatch_async(dbQueue) {
                    if !ieachDBValue {
                        numProcessed++
                    }
                }
            }
        }


// case 4
for i in 31...40 {

            dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_UTILITY.value), 0)) {
            let eachDBValue = ConnectedDevices.getAllDetails(i)

                dispatch_async(dbQueue) {
                    if !eachDBValue {
                        numProcessed++
                    }
                }
            }
        }

So if in case 1, if result is false for 1 to 10 then it should go to case 2. If result is true for any instance.. it should not execute any case 2,3,4.

Similarly for case 2, if result is false for 1 to 10 then it should go to case 3 else it should stop. All this i need to do based on the conditions.

like image 739
cybergeeeek Avatar asked Oct 31 '22 19:10

cybergeeeek


1 Answers

This is my solution

The fake ConnectedDevices class

This is the fake class I created to test your scenario. As you can see the method getAllDetails does simulate your database access. It waits for 10 seconds and then returns true only if the input parameter is 40. This way we can test this code on the worst case scenario since all the call will need to be done.

class ConnectedDevices {
    class func getAllDetails(i:Int) -> Bool {
        sleep(10)
        return i == 40
    }
}

The Loader class

This is the class that will interact with Grand Central Dispatch.

class Loader {
    private var numProcessed = 0
    private let dbQueue = dispatch_queue_create("dbQueue", DISPATCH_QUEUE_SERIAL)
    private let utilityQueue = dispatch_get_global_queue(QOS_CLASS_UTILITY, 0)

    func search(completion:(result:Int?)->()) {
        dispatch_async(utilityQueue) {
            for group in [1, 11, 21, 31] {
                if let indexFound = self.search10(group) {
                    completion(result: indexFound)
                    return
                }
            }
            completion(result: nil)
        }
    }

    private func search10(startingIndex:Int) -> Int? {
        var indexFound : Int?
        dispatch_apply(10, utilityQueue) { (delta) -> () in
            let found = ConnectedDevices.getAllDetails(startingIndex + delta)
            dispatch_sync(self.dbQueue) {
                self.numProcessed++ // this is increased anyway
                if found {
                    indexFound = startingIndex + delta
                }
            }
        }
        return indexFound
    }
}

search10(startingIndex:Int)

This synchronous method receives an Int as param and executes 10 concurrent calls to ConnectedDevices.getAllDetails. Each call is performed adding startingIndex to delta (delta goes from 0 to 9).

The result is synchronously put into a synchronous queue where numProcessed is safely increased. If found is true then the "winning" index (startingIndex + delta) is returned.

If found never becomes true then nil is returned.

search()

This is an asych method. It's a simple for loop where the group variables is populated with 1, then 11, then 21 and finally 31.

Each time search10 is invoked. If it does return an Int the value is passed to the completion closure, otherwise the loop does continue.

If the 4 cycles are performed with no value returned from search10 then nil is passed to the completion closure.

Usage

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        debugPrintln("Begin: \(NSDate())")
        Loader().search { (result) -> () in
            debugPrintln(result)
            debugPrintln("End: \(NSDate())")
        }
    }    
}

Test

40 sequential calls to ConnectedDevices.getAllDetails(...) would require 40 * 10 = 400 seconds.

In my iPhone Simulator without any optimization enabled this code requires about 120 seconds.

Hope this is what you need.

P.S.

  1. Please note that I am increasing numProcessed also if the current result is true.
  2. I did not found the purpose of isConditionTrue so I removed it.
like image 128
Luca Angeletti Avatar answered Nov 15 '22 07:11

Luca Angeletti