Используя Grand Central Dispatch, как я могу проверить, есть ли уже запущенный блок?
Я использую GCD, чтобы сделать некоторую фоновую загрузку из Интернета. Это прекрасно работает, за исключением небольшого недостатка. В моем приложении есть 3 вкладки, и при нажатии на любую вкладку GCD начинает выполнять фоновую загрузку для соответствующей вкладки. Если пользователь решает перейти с первой вкладки на вторую вкладку (когда GCD начал загружать данные для первой вкладки), а затем снова возвращается к первой вкладке. GCD запустит еще один фоновый поток (хотя первый фоновый поток еще не закончил загрузку данных).
Так есть ли способ проверить, работает ли фоновый поток в данный момент? Так что он не запускает несколько фоновых потоков, если пользователь решил бы переключать вкладки назад и вперед очень быстро (по какой-то причине).
2 ответа
Если вы хотите предотвратить одновременный запуск двух блоков одного типа, вы можете использовать диспетчерский семафор. Если для семафора установлено значение 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);
Если вы создадите подобный семафор для операций загрузки каждой вкладки, вы можете проверить, чтобы убедиться, что более одной загрузки не ставится в очередь одновременно для этой вкладки.
Вам нужен простой логический флаг, чтобы избежать повторного запуска задачи, пока она не будет завершена (вообще не включает 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 (вы сами?).
Обратите внимание, что в этом примере я использую свойство, а не простую переменную доступа. Это позволяет вам реализовывать более интересные вещи в установщике, такие как активация индикатора активности, отключение ввода пользовательского интерфейса и т. Д. И это также работает с подклассами, которые выполняют свою функцию, когда состояние переменной изменяется в родительском классе.