3

I have a series of dispatch_async that I am performing and I would like to only update the UI when they are all done. Problem is the method within dispatch_async calls something in a separate thread so it returns before the data is fully loaded and dispatch_group_notify is called before everything is loaded.

So I introduce a infinite loop to make it wait until a flag is set. Is this the best way? See code below.

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_group_t group = dispatch_group_create();

for (...) {
  dispatch_group_async(group, queue, ^{
    __block BOOL dataLoaded = NO;

    [thirdPartyCodeCallWithCompletion:^{
       dataLoaded = YES;
    }];

    // prevent infinite loop
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), 
    queue, ^{
          dataLoaded = YES;
    });

    // infinite loop to wait until data is loaded
    while (1) {
       if (dataLoaded) break;
    }
  }

  dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    //update UI
  });
}
Boon
  • 40,656
  • 60
  • 209
  • 315
  • 2
    Infinite loops are never the best way :) Try a dispatch_semaphore_t instead; see [How do I wait for an asynchronously dispatched block to finish?](http://stackoverflow.com/questions/4326350/how-do-i-wait-for-an-asynchronously-dispatched-block-to-finish/4326754#4326754) – jatoben May 12 '14 at 19:54

2 Answers2

9

You're already aware of dispatch groups. Why not just use dispatch_group_wait(), which includes support for a timeout? You can use dispatch_group_enter() and dispatch_group_leave() rather than dispatch_group_async() to make the group not done until the internal block for the third-party call with completion is finished.

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_group_t group = dispatch_group_create();

for (...) {
  dispatch_group_enter(group);
  dispatch_async(queue, ^{
    [thirdPartyCodeCallWithCompletion:^{
       dispatch_group_leave(group);
    }];
  }
}

dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, NSECS_PER_SEC));
dispatch_async(dispatch_get_main_queue(), ^{
  //update UI
});

The use of dispatch_group_wait() does make this code synchronous, which is bad if run on the main thread. Depending on what exactly is supposed to happen if it times out, you could use dispatch_group_notify() as you were and use dispatch_after() to just updates the UI rather than trying to pretend the block completed.


Update: I tweaked my code to make sure that "update UI" happens on the main queue, just in case this code isn't already on the main thread.

By the way, I only used dispatch_async() for the block which calls thirdPartyCodeCallWithCompletion: because your original used dispatch_group_async() and I wasn't sure that the hypothetical method was asynchronous. Most APIs which take a completion block are asynchronous, though. If that one is, then you can just invoke it directly.

Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • Hi Ken, I use your proposed method but the code is stuck - update UI is never called. I don't have this issue with the semaphore approach. – Boon May 13 '14 at 13:47
  • I messed up the timeout parameter to `dispatch_group_wait()`. I was passing a delta but it needs an absolute time. I have fixed the code. – Ken Thomases May 14 '14 at 06:28
8

Another method is to use semaphore and the dispatch_semaphore_wait:

// Create your semaphore, 0 is specifying the initial pool size
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    @autoreleasepool {

    // Your code goes here

    }
   // Release the resource and signal the semaphore
   dispatch_semaphore_signal(semaphore);
 });

// Wait for the above block execution, AKA Waits for (decrements) a semaphore.
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// After this line you can now safely assert anything you want regarding the async operation since it is done.
OhadM
  • 4,687
  • 1
  • 47
  • 57
  • This causes program hanging forever if switched to another thread. This is almost like `dispatch_sync` – Vannes Yang Jul 07 '21 at 06:29
  • Not sure how your code looks like but it really depends how you use it, after all, it's a snip code – OhadM Jul 07 '21 at 11:29