Многопоточность в iOS - как заставить поток ждать условия? - PullRequest
6 голосов
/ 06 марта 2012

Я создаю приложение, которое извлекает набор результатов из базы данных - я использую MBProgressHUD , чтобы показать ход выполнения запроса с анимацией.Метод, который я использую, вызывает анимацию при выполнении метода в другом потоке, и, как только это будет сделано, анимация будет скрыта.Мой вопрос после вызова:

[HUD showWhileExecuting:@selector(getResults) onTarget:self withObject:nil animated:YES];

Я хотел бы, если нет результатов, отобразить предупреждение об этом, и, если есть, загрузить следующий вид.Пока у меня есть этот код:

[HUD showWhileExecuting:@selector(getResults) onTarget:self withObject:nil animated:YES];

if(self.thereAreEvents) {
    [self performSegueWithIdentifier:@"searchResults" sender:self];
} else {
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"No results" message:@"Sorry, there are no results for your search. Please try again." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
    [alert show];
    [alert release];
}

self.thereAreEvents устанавливается в конце метода getResults.Однако, поскольку этот метод вызывается в другом потоке, эта строка выполнения продолжается и показывает предупреждение, даже если в базе данных есть события.

Итак, у меня есть два вопроса: Какой самый простой способ?реализовать механизм ожидания сигнала в iOS, и как наиболее эффективно реализовать такой механизм в iOS?

Спасибо!

Ответы [ 5 ]

11 голосов
/ 06 марта 2012

Вы можете использовать цикл ожидания занятости для быстрого и грязного решения:

__block BOOL finished = NO;
dispatch_async(/* global queue */, ^{
    // …
    finished = YES;
});
while (!finished) /* waiting */;

В «реальном» коде лучше использовать семафор:

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(/* global queue */, ^{
    // …
    dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_release(sempahore);

Это лучше, чем цикл занятости, поскольку заблокированный поток не потребляет процессорное время.

Лучшее решение - не блокировать и не перестраивать код для асинхронной работы. В вашем случае вы должны отобразить счетчик и начать загрузку данных. Когда загрузка данных закончится, вы должны получить асинхронный обратный вызов (либо с помощью блока, либо обратного вызова цели / действия) и отобразить результаты, либо показать предупреждение об ошибке. Блокирование с помощью занятого цикла или семафора в этом случае является решением бедного человека.

2 голосов
/ 06 марта 2012

Вы также можете рассмотреть NSConditionLock.

Так что это будет примерно так в потоке 1:

[conditionLock lockWhenCondition:kConditionOkayToProceed];
[conditionLock unlockWithCondition:kConditionGettingResults];

[HUD show...]

[conditionLock lockWhenCondition:kConditionResultsFetched];
[conditionLock unlockWithCondition:kConditionOkayToProceed];

И в HUD:

- (void)show...
{
    [conditionLock lockWhenCondition:kConditionGettingResults];

    // stuff here

    [conditionLock unlockWithCondition:kConditionResultsFetched];
}

Хотя гораздо лучшим решением было бы передать блок или цель / селектор в HUD, который он должен выполнять при получении результатов.

РЕДАКТИРОВАТЬ: так что вы быв итоге получится код вроде:

[HUD showWhileExecuting:@selector(getResults)
     onTarget:self
     withObject:nil
     animated:YES
     performWhenFinished:
     ^{
         if(self.thereAreEvents) {
             [self performSegueWithIdentifier:@"searchResults" sender:self];
         } else {
             UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"No results" message:@"Sorry, there are no results for your search. Please try again." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
             [alert show];
             [alert release];
         }
      }];

А в HUD:

- (void)showWhile... performWhenFinished:(dispatch_block_t)block
{
     // all the other stuff you were going to do here, then
     // eventually...

     // if no guarantees, maybe just:
     block();

     // otherwise, if promised to dispatch to the main queue:
     dispatch_async(dispatch_get_main_queue(), block);
}

С HUD, обладающим дополнительным интеллектом, для принятия dispatch_block_t в качестве окончательного аргумента и вызова его, когдарезультаты в (гарантирует ли отправка обратно в основной поток или иным образом).

0 голосов
/ 24 февраля 2014

вы можете использовать dispatch_time_t как ..

double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    <#code to be executed on the main queue after delay#>
});
0 голосов
/ 06 марта 2012

Это ваш путь: Руководство по программированию параллелизма

Также: Синхронизация

Более резко: Использование замков . Я думаю, что последний мог оказать лучшую помощь.

Еще один простой подход, это плохо, но работает

NSAssert(![NSThread isMainThread], @"Do not run on MAIN thread");
while (yourCondition) { [NSThread sleepForTimeInterval:0.2]; }
0 голосов
/ 06 марта 2012

Не уверен, что это лучший способ сделать это, но я бы попробовал использовать цикл while для наблюдения некоторого логического значения с чем-то вроде [NSThread sleepForTimeInterval: 1];внутри цикла.

Возможно также установить таймаут.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...