Wait for many asynchronous calls to perform callback

核能气质少年 提交于 2019-12-12 11:51:58

问题


I want to synchronize some data with a web service. For each item I have to make a asynchronous call.

I want to have a completion block witch is called, when each item was synchronized. For each item I am able to perform a completion block. Now, I don't know a good way how to do it.

This is the interface:

-(void) synchronizeItemsOnComplete:(CompleteBlock) block {
    NSArray* items = // get items
    for (int i = 0, n = [items count]; i < n; i++) {
       [self synchronizeItem:[items objectAtIndex:i] onComplete:^{
          // What do do here?
       }];
    }
    // And/or here?
}

-(void) synchronizeItemOnComplete:(CompleteBlock) block {
    // do something
    block();
}

How can I wait for the synchronization and then perform the block?

I tried something like this:

NSArray* items = // get items

__block int countOfItemsUntilDone = [items count];

for (int i = 0, n = countOfItemsUntilDone; i < n; i++) {
    [self synchronizeItem:[items objectAtIndex:i] onComplete:^{
        countOfItemsUntilDone--;
    }];
}

dispatch_queue_t queue = dispatch_queue_create("wait for syncing", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
    while (countOfItemsUntilDone > 0) {
        usleep(1000); // wait a little bit
    }
    block();
});
dispatch_release(queue);

But I think this is a quite bad way. Any ideas?


回答1:


Instead of spinning in a loop waiting for the counter to equal zero, check the counter value each time you decrement it, then fire an event when it reaches zero.

 -(void) synchronizeItemsOnComplete:(CompleteBlock) block {
    NSArray* items = // get items
    __block NSUInteger remaining = [items count];

    for (ItemClass* item in items) {
       [self synchronizeItemImage:item onComplete:^{
            --remaining;
            if (remaining == 0) {
                block();
            }
       }];
    }
 }

To explain why it feels wrong, there are two things you're doing here that you should do either never or rarely:

  • Using background queues. This is difficult and bug-prone. Don't do it without reading up a lot about writing concurrent code. You also only really need to do this if an operation blocks for a substantial amount of time (eg., to read a file from disk, or perform an intensive calculation). Don't assume you need to do it unless you have a good reason (eg., a measurable performance problem).

  • Spinning in a loop, checking a variable for changes and calling sleep. You should never do this.

Also, if you're looping over the elements in an array, the for ... in syntax is much nicer (and potentially more efficient) calling objectAtIndex: on each index.




回答2:


Never check or decrement shared memory in different threads like this, it can cause races. Use a dispatch group to do what you're doing.

dispatch_queue_t myBGQueue;
dispatch_group_t itemsGroup = dispatch_group_create();

for (ItemClass *item in items) {
    dispatch_group_async(itemsGroup, myBGQueue, ^{
        [self synchronizeItemImage:item];
    });
}

/* execution will sleep here until all the blocks added in the `for` complete */
dispatch_group_wait(itemsGroup, DISPATCH_TIME_FOREVER);

dispatch_release(itemsGroup);



回答3:


You can use these to use synchronously.

GCD and this

performSelector:waitUntilDone:YES



来源:https://stackoverflow.com/questions/15071647/wait-for-many-asynchronous-calls-to-perform-callback

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!