Используя Grand Central Dispatch, как я могу проверить, есть ли уже запущенный блок?

Я использую GCD для загрузки некоторой информации из Интернета. Это отлично работает, за исключением небольшого недостатка. В моем приложении у меня есть 3 вкладки, и при нажатии на любую вкладку GCD начинает выполнять загрузку фона для соответствующей вкладки. Если пользователь решает перейти от первой вкладки ко второй вкладке (когда GCD начал загружать данные для первой вкладки), а затем снова возвращается к первой вкладке. GCD запустит другой фоновый поток (хотя первый фоновый поток еще не закончил загружать данные).

Итак, есть ли способ проверить, работает ли фоновый поток? Чтобы он не запускал несколько фоновых потоков, если пользователь решил бы быстро переключать вкладки назад и вперед (по какой-то причине).

Ответ 1

Если вы хотите одновременно запретить запуск двух блоков одного и того же типа, вы можете использовать семафор отправки. Если семафор установлен в число 1, вы можете проверить семафор перед тем, как отпустить блок и залог, если что-то еще работает. В конце блока вы сигнализируете семафор, чтобы разрешить отправку других блоков.

Я делаю это в одном из моих приложений, чтобы предотвратить добавление в очередь нескольких блоков рендеринга OpenGL ES в очереди (предотвращение наращивания блоков в очереди, если кадр занимает больше 1/60-й секунды до визуализации). Я опишу часть этого в моем ответе здесь, используя следующий код:

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

dispatch_async(openGLESContextQueue, ^{

    [EAGLContext setCurrentContext:context];

    // Render here

    dispatch_semaphore_signal(frameRenderingSemaphore);
});

где frameRenderingSemaphore создается ранее следующим образом:

frameRenderingSemaphore = dispatch_semaphore_create(1);

Если вы создаете аналогичный семафор для каждой операции загрузки табуляции, вы можете сделать чек, чтобы убедиться, что несколько экземпляров одной очереди не помещаются в очередь за эту вкладку.

Ответ 2

Вам нужен простой логический флаг, чтобы избежать повторного запуска задачи, пока она не будет завершена (вообще не включает GDC). Что-то вроде (псевдокод, не проверено, отказ от ответственности и т.д.):

- (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;
    }
}

Этот псевдо-код будет работать для таких вещей, как асинхронное NSURLConnection. Я до сих пор не изучил GDC, кому-то еще нужно будет адаптировать это к GDC (себе?).

Обратите внимание, что в этом примере я использую свойство, а не простой доступ к переменной. Это позволяет вам реализовать более интересные вещи в сеттере, например, активировать индикатор активности, отключить ввод пользовательского интерфейса и т.д. И он также работает с подклассами, выполняющими свою задачу, когда состояние переменной изменяется в родительском классе.