1

I have code set up like below. It is my understanding that queue1 should finish all operations THEN move to queue2. However, as soon as my async operation starts, queue2 begins. This defeats the purpose of GCD.. what am I doing wrong? This outputs:

did this finish
queue2
then some time later, prints success from image download

..I want to make it clear that if I put in other code in queue1, such as print("test") or a loop 0..10 printing i, all those operations will complete before moving to queue2. It seems the async download is messing with it, how can I fix this? There is no documentation anywhere, I used This very hepful guide from AppCoda http://www.appcoda.com/grand-central-dispatch/

    let queue1 = DispatchQueue(label: "com.matt.myqueue1")
    let queue2 = DispatchQueue(label: "com.matt.myqueue2")
    let group1 = DispatchGroup()
    let group2 = DispatchGroup()

    let item = DispatchWorkItem{
        // async stuff happening like downloading an image  
        // print success if image downloads       
    }

    queue1.sync(execute: item)

    item.notify(queue1, execute: {
        print("did this finish?")
    })


    queue2.sync {
        print("queue2")
    }
MattEm
  • 181
  • 1
  • 8
  • These questions might help: [here](https://stackoverflow.com/questions/19822700/difference-between-dispatch-async-and-dispatch-sync-on-serial-queue) and [here](https://stackoverflow.com/questions/19179358/concurrent-vs-serial-queues-in-gcd) They are objective-C but the logic of async and queue is exactly the same – mfaani Jan 05 '17 at 21:24
  • Also do the groups have any purpose in your examples? Is it a typo or am I missing something? – mfaani Jan 05 '17 at 21:28
  • @Honey I tried to delay execution of the async task with groups.. can you explain why queue2 is executing before queue1 finishes its task? – MattEm Jan 05 '17 at 21:47
  • @Honey the logic in your first link is flawed.. these are sync so regardless of execution time, "success" should ALWAYS print before "queue2", but that is not the case – MattEm Jan 05 '17 at 21:51
  • :D Try it and see if it if is flawed. The person has 28k reputation and 280 upvotes so 100% not flawed. read the comments below Brain's answer. I actually commented there back and forth. It's a very very complicated topic. It took me months to figure out and still I'm not 100% if I know how multi-threading works...Also see [this](https://www.youtube.com/watch?v=Uf0s3LTaj3k) – mfaani Jan 05 '17 at 22:08

2 Answers2

1
let item = DispatchWorkItem{
    // async stuff happening like downloading an image  
    // print success if image downloads       
}

OK, defines it, but nothing runs yet.

queue1.sync(execute: item)

Execute item and kick off its async events. Immediately return after that. Nothing here says "wait for those unrelated asynchronous events to complete." The system doesn't even have a way to know that there are additional async calls inside of functions you call. How would it know whether object.doit() includes async calls or not (and whether those are async calls you meant to wait for)? It just knows when item returns, continue.

This is what group1 is supposed to be used for (you don't seem to use it for anything). Somewhere down inside these "async stuff happening" you're supposed to tell the system that it finished by leaving the group. (I have no idea what group2 is for. It's never used either.)

item.notify(queue1, execute: {
    print("did this finish?")
})

item already finished. We know it has to have finished already, because it was run with sync, and that doesn't return until its item has. So this block will be immediately scheduled on queue1.

queue2.sync {
    print("queue2")
}

Completely unrelated and could run before or after the "did this finish" code, we schedule a block on queue2.

What you probably meant was:

let queue1 = DispatchQueue(label: "com.matt.myqueue1")
let group1 = DispatchGroup()

group1.enter()

// Kick off async stuff.
// These usually return quickly, so there's no need for your own queue.
// At some point, when you want to say this is "done", often in some 
// completion handler, you call group1.leave(), for example:
       ... completionHandler: { group1.leave() }

// When all that finishes, print
group.notify(queue: queue1) { print("did this finish?") }
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • Is my answer incorrect? I *think* we are saying the same thing. – mfaani Jan 06 '17 at 00:06
  • @Honey I had trouble following your answer. It's likely we're covering the same territory, but the mother/child discussion seemed to address a different issue. Certainly possible you're clearer than I am to the OP and I'm just not reading well. – Rob Napier Jan 06 '17 at 00:33
  • I'm not certain of my answer :| but I made some edits...can you comment on why the mother/child discussion is different. Isn't that the core of his question? – mfaani Jan 06 '17 at 00:44
  • @RobNapier, first, I want to say thank you for your answer. I have a much more specific post here on the actual async call(s): http://stackoverflow.com/questions/41493311/grand-central-dispatch-multiple-dispatchgroups Would you mind taking a look there? I'm going to accept this answer, because this reaffirms groups were the way to go.. need help using multiple groups though.. – MattEm Jan 09 '17 at 18:54
0

EVERYTHING is initially queued from the main queue, however at some point you switch from main queue to a background queue and you should NOT expect a synchronized queue would wait for what is has enqueued on another queue. They are irrelevant. If that was the case then always and always no matter what, everything is to wait for whatever it asked to run.*

so here's what I see is happening.

queue1 is happily finished, it has done everything it was suppose to by enqueuing item on another queue <-- that's all it was suppose to do. Since the 'async stuff' is async... it won't wait for it to finish. <-- actually if you use breakpoints inside the async you would see that the breakpoints would jump to } meaning they don't wait for a background queue they just jump to the end of the queue, since they are no longer on the main thread

then since it was a sync queue it wall wait till it's done. Once done it will go through its notify...now here's where it get's tricky: depending on how fast what you do in the async... 'print success' will either get called first or "queue2" though here obviously queue2 is returning/finishing sooner.

similar to what you see in this answer.


*: A mother (main queue) tells it's child1 to your room and bring book1 back, then tells child2 to your room and bring book2 back, then tells child3 to your room and bring book3 back. Each child is being ran from its own queue ( not the mother's queue).

The mother doesn't wait for child1 to come back...so it can tell child2 to go. The mother only tells child 1 go...then child2 go...then child 3 go.

However child2 is not told (to go) before child 1, nor child3 is told before child2 or child1 <-- this is because of the serial-ness of main queue. They get dispatched serially, but their completion order depends on how fast each child/queue finishes

mfaani
  • 33,269
  • 19
  • 164
  • 293