Peter Warbo
Peter Warbo

Reputation: 11728

Using Grand Central Dispatch, how can I check if there is a block already running?

I'm using GCD to do some background loading from the internet. This works great except for a little flaw. In my app I have 3 tabs and when clicking on any tab the GCD starts to do the background loading for the appropriate tab. If the user decides to go from the first tab to the second tab (when the GCD has started downloaded data for the first tab) and then returns to the first tab again. GCD will start another background thread (even though the first background thread hasn't finished downloaded the data yet).

So is there a way to check if a background thread is currently running? So that it doesn't start multiple background threads if the user would choose to switch tabs back and forth very quickly (for some reason).

Upvotes: 2

Views: 2000

Answers (2)

Brad Larson
Brad Larson

Reputation: 170317

If you wanted to prevent two blocks of the same type from running at the same time, you could use a dispatch semaphore. With a semaphore set to a count of 1, you can check the semaphore before firing off the block and bail if something is still running. At the end of the block, you signal the semaphore to allow other blocks to be submitted.

I do this in one of my applications to prevent more than one OpenGL ES frame rendering block to be added to the queue at once (preventing buildup of blocks in the queue if a frame takes longer than 1/60th of a second to render). I describe some of this in my answer here, using the following code:

if (dispatch_semaphore_wait(frameRenderingSemaphore, DISPATCH_TIME_NOW) != 0)
{
    return;
}

dispatch_async(openGLESContextQueue, ^{

    [EAGLContext setCurrentContext:context];

    // Render here

    dispatch_semaphore_signal(frameRenderingSemaphore);
});

where frameRenderingSemaphore is created earlier as follows:

frameRenderingSemaphore = dispatch_semaphore_create(1);

If you create a similar semaphore for each tab's download operations, you could do a check to make sure that more than one download isn't being queued up at a time for that tab.

Upvotes: 10

Grzegorz Adam Hankiewicz
Grzegorz Adam Hankiewicz

Reputation: 7681

What you need is a simple boolean flag to avoid starting the task again until it is complete (doesn't involve GDC at all). Something like (pseudo-code, not tested, disclaimer, etc):

- (void)something_you_run_in_your_view_did_appear
{
    synchronize(self) {
        if (self.doing_task)
            return;
        self.doing_task = YES;
    }
    start_your_task_here
}

- (void)something_you_run_when_the_task_finishes
{
    synchronize(self) {
        self.doing_task = NO;
    }
}

This pseudo code would work for things like an asynchronous NSURLConnection. I still haven't explored GDC yet, somebody else will need to adapt this to GDC (yourself?).

Note that in this example I'm using a property rather than a simple variable access. This allows you to implement fancier things in the setter, like activating an activity indicator, disabling user interface input, etc. And it also works with subclasses doing their thing when the state of the variable changes in the parent class.

Upvotes: 0

Related Questions