0

I have just started reading swift and i'm currently confused of how to use threading correctly.

What i'm trying to achieve in the following block of code, is to execute the print statements inside the dispatchers, but i want to do it in order. The problem that i have, is that of course i want to do this in a background thread than the main since this is a long task, and at the same time to execute it in order while i'm giving delay in the execution. The current block executes each of the cases all together.

I have also took a look in Timer and Semaphores but without any results.

Any help or explanation of what i'm doing wrong or what should i approach will be appreciated.

let formattedSeries = ["a", "a", "b"]
let dispatchQueue = DispatchQueue(label: "taskQueue")
let a = 1000
let b = 5000
for (index, letter) in (formattedSeries.enumerated()){

    switch letter {
    case "a":
        dispatchQueue.asyncAfter(deadline: .now() + .milliseconds(a), execute: {
            print("a executed")           
        })      
        break
    case "b":
        dispatchQueue.asyncAfter(deadline: .now() + .milliseconds(b), execute: {
            print("b executed")
        })
        break
    default:
        print("default")
    }
}
Makis
  • 1,214
  • 3
  • 16
  • 40
  • Can you please clarify what do you mean by 'order'? Is this order dictated by the loop (each iteration is an operation that should be finished before the next one fires up) or by its delay? – Alladinian Jan 26 '20 at 16:44
  • Hi, i mean by the loop, for example if my formattedSeries consists of a,b i expect to first see "a executed" after 1000 milliseconds and when this is finished i expect to see "b executed" after 5000 milliseconds and so on.. regarding what is inside the array – Makis Jan 26 '20 at 16:49
  • Is the delay something that is actually needed or you just use it as an attempt to dictate the order of operations? – Alladinian Jan 26 '20 at 16:51
  • Unfortunately is something that i need. – Makis Jan 26 '20 at 16:53

2 Answers2

1

To execute the tasks in order you need an asynchronous operation.

  1. Use the class AsynchronousOperation provided in this answer
  2. Create a serial OperationQueue and set maxConcurrentOperationCount to 1
  3. Subclass AsynchronousOperation and put the dispatchQueue.asyncAfter tasks into the main() method of the subclass. Call finish() in the closure (before or after print("a executed"))
  4. Add the operations to the serial operation queue
vadian
  • 274,689
  • 30
  • 353
  • 361
1

You can use a dispatch group to force the evaluation of the next letter to wait for evaluation of the previous letter:

let dispatchGroup = DispatchGroup()
let dispatchQueue = DispatchQueue(label: "taskQueue")
let a = 1000
let b = 5000
let formattedSeries = "abbaabba"
print("start", Date().timeIntervalSince1970)
for (index, letter) in (formattedSeries.enumerated()){
    dispatchGroup.enter()
    switch letter {
    case "a":
        dispatchQueue.asyncAfter(deadline: .now() + .milliseconds(a), execute: {
            print("a executed", Date().timeIntervalSince1970)
            dispatchGroup.leave()
        })
        break
    case "b":
        dispatchQueue.asyncAfter(deadline: .now() + .milliseconds(b), execute: {
            print("b executed", Date().timeIntervalSince1970)
            dispatchGroup.leave()
        })
        break
    default:
        print("default")
    }
    dispatchGroup.wait()
}

I've added some extra output to prove that the intervals are correct. The output is

start 1580060250.3307471
a executed 1580060251.389974
b executed 1580060256.889923
b executed 1580060262.2758632
a executed 1580060263.372933
a executed 1580060264.373787
b executed 1580060269.37443
b executed 1580060274.375314
a executed 1580060275.4726748

which proves that we evaluated the letters in order, and that the async_after intervals elapse between the prints.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Hey matt, many thanks, this fixes my problem. I'm going to read a bit about dispatch groups cause i'm totally unaware, i suppose that this is still a background thread right? – Makis Jan 26 '20 at 17:42
  • 2
    I don't know what you mean by "still a background thread". At the moment, just to generate the syntax with minimal change, we are `wait`-ing on the main thread, which is obviously poor practice. But you can wrap the whole thing in a DispatchQueue `async` call to put it into a background thread. – matt Jan 26 '20 at 18:10