Does dispatch_async(dispatch_get_main_queue(), ^{...}); wait until done?

EmptyStack picture EmptyStack · Jul 5, 2013 · Viewed 110.3k times · Source

I have a scenario in my app, where I want to do some time consuming task which consists of some data processing as well as UI update, in a method. My method looks like this,

- (void)doCalculationsAndUpdateUIs {

    // DATA PROCESSING 1
    // UI UPDATE 1

    // DATA PROCESSING 2
    // UI UPDATE 2

    // DATA PROCESSING 3
    // UI UPDATE 3
} 

As it is time consuming I wanted to do the data processing on the background thread, using,

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{

But as both data processing and UI updates are in the same method, I wanted to move only the UI updates in main thread using,

dispatch_async(dispatch_get_main_queue(), ^{

Finally my method looks like this,

- (void)doCalculationsAndUpdateUIs {

    // DATA PROCESSING 1 
    dispatch_async(dispatch_get_main_queue(), ^{
        // UI UPDATE 1
    });

    /* I expect the control to come here after UI UPDATE 1 */

    // DATA PROCESSING 2
    dispatch_async(dispatch_get_main_queue(), ^{
        // UI UPDATE 2
    });

    /* I expect the control to come here after UI UPDATE 2 */

    // DATA PROCESSING 3
    dispatch_async(dispatch_get_main_queue(), ^{
        // UI UPDATE 3
    });
}

Does this really work? Is this really a good practice? What is the best way to achieve this?

P.S. All these three operations are interrelated to each other.


EDIT: Sorry guys. I have missed a line in the above code. My actual code looks like this.

- (void)doCalculationsAndUpdateUIs {

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        // DATA PROCESSING 1 
        dispatch_async(dispatch_get_main_queue(), ^{
            // UI UPDATE 1
        });

        /* I expect the control to come here after UI UPDATE 1 */

        // DATA PROCESSING 2
        dispatch_async(dispatch_get_main_queue(), ^{
            // UI UPDATE 2
        });

        /* I expect the control to come here after UI UPDATE 2 */

        // DATA PROCESSING 3
        dispatch_async(dispatch_get_main_queue(), ^{
            // UI UPDATE 3
        });
    });
}

Once again, I really apologize for the confusion.

Answer

David Rönnqvist picture David Rönnqvist · Jul 5, 2013

No it doesn't wait and the way you are doing it in that sample is not good practice.

dispatch_async is always asynchronous. It's just that you are enqueueing all the UI blocks to the same queue so the different blocks will run in sequence but parallel with your data processing code.

If you want the update to wait you can use dispatch_sync instead.

// This will wait to finish
dispatch_sync(dispatch_get_main_queue(), ^{
    // Update the UI on the main thread.
});

Another approach would be to nest enqueueing the block. I wouldn't recommend it for multiple levels though.

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    // Background work

    dispatch_async(dispatch_get_main_queue(), ^{
        // Update UI

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            // Background work

            dispatch_async(dispatch_get_main_queue(), ^{
                // Update UI
            });
        });
    });
});

If you need the UI updated to wait then you should use the synchronous versions. It's quite okay to have a background thread wait for the main thread. UI updates should be very quick.