Как ждать окончания потока в Objective-C - PullRequest
14 голосов
/ 07 октября 2010

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

Ответы [ 7 ]

39 голосов
/ 23 ноября 2012

Вот еще один способ сделать это с помощью GCD:



- (void)main
{
    [self doStuffInOperations];
}

- (void)doStuffInGCD
{
    dispatch_group_t d_group = dispatch_group_create();
    dispatch_queue_t bg_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_group_async(d_group, bg_queue, ^{
        [self doSomething:@"a"];
    });

    dispatch_group_async(d_group, bg_queue, ^{
        [self doSomething:@"b"];
    });

    dispatch_group_async(d_group, bg_queue, ^{
        [self doSomething:@"c"];
    });


    // you can do this to synchronously wait on the current thread:
    dispatch_group_wait(d_group, DISPATCH_TIME_FOREVER);
    dispatch_release(d_group);
    NSLog(@"All background tasks are done!!");


    // ****  OR  ****

    // this if you just want something to happen after those are all done:
    dispatch_group_notify(d_group, dispatch_get_main_queue(), ^{
        dispatch_release(d_group);
        NSLog(@"All background tasks are done!!");        
    });
}

- (void)doSomething:(id)arg
{
    // do whatever you want with the arg here 
}
9 голосов
/ 15 мая 2012

Используйте NSOperationQueue, как это
(из памяти, так что простите за любые мелкие ошибки - вы получите основную идею):


// ivars
NSOperationQueue *opQueue = [[NSOperationQueue alloc] init];
// count can be anything you like
[opQueue setMaxConcurrentOperationCount:5];

- (void)main
{
    [self doStuffInOperations];
}

// method
- (void)doStuffInOperations
{
    // do parallel task A
    [opQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSomething:) object:@"a"] autorelease]];

    // do parallel task B
    [opQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSomething:) object:@"b"] autorelease]];

    // do parallel task C
    [opQueue addOperation:[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSomething:) object:@"c"] autorelease]];


    [opQueue waitUntilAllOperationsHaveFinished];

    // now, do stuff that requires A, B, and C to be finished, and they should be finished much faster because they are in parallel.
}

- (void)doSomething:(id)arg
{
    // do whatever you want with the arg here 
    // (which is in the background, 
    // because all NSOperations added to NSOperationQueues are.)
}


6 голосов
/ 07 октября 2010

Мое первое желание - не делать то, что вы предлагаете. Техника, которую я использовал ранее, заключается в том, чтобы дать потоку селектор для метода в исходном объекте (который находится в основном потоке). Когда запускается второй поток, основной поток продолжает выполняться, но отображает какой-то индикатор занятости на дисплее. Это позволяет продолжить взаимодействие с пользователем, если требуется.

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

Я успешно использовал это для приложения, которое обращается к веб-службе (во втором потоке), а затем обновляет отображение, как только данные возвращаются без его блокировки. Это делает пользовательский опыт намного приятнее.

5 голосов
/ 21 марта 2014

Для таких случаев я обычно использую класс NSCondition.

//this method executes in main thread/queue
- (void)waitForJob
{
    id __weak selfWeak = self;
    NSCondition *waitHandle = [NSCondition new];
    [waitHandle lock];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        [selfWeak doSomethingLongtime];
        [waitHandle signal];
    });
    //waiting for background thread finished
    [waitHandle waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:60]];
}
1 голос
/ 18 августа 2013

Несколько технических способов синхронизации многопотоков, таких как NSConditionLock (mutex-lock), NSCondition (семафор) ut Но они являются общими знаниями программирования для других языков (java ...), кроме target-c. Я предпочитаю вводить run loop (специально для Какао) для реализации объединения потоков:

NSThread *A; //global
A = [[NSThread alloc] initWithTarget:self selector:@selector(runA) object:nil]; //create thread A
[A start];

- (void)runA    
{
  [NSThread detachNewThreadSelector:@selector(runB) toTarget:self withObject:nil]; //create thread B    
  while (1)    
  {    
    if ([[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) //join here, waiting for thread B    
    {    
      NSLog(@"thread B quit...");    
      break;    
    }    
  }    
}

- (void)runB    
{    
  sleep(1);    
  [self performSelector:@selector(setData) onThread:A withObject:nil waitUntilDone:YES modes:@[NSDefaultRunLoopMode]];    
}
0 голосов
/ 16 августа 2013

Моя техника состоит в том, чтобы проверить, может ли задача выполняться (если фоновый поток завершен), и если она не может быть запущена, тогда я использую GCD, чтобы повторить попытку после задержки:

- (void)doSomething
{
  if (/* is the other thread done yet? */) {
    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){
      [self doSomething];
    });
    return;
  }

  // do something
}
0 голосов
/ 07 октября 2010

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

BOOL isThreadRunning = NO;
- (void)beginThread {   
    isThreadRunning = YES;

    [self performSelectorInBackground:@selector(backgroundThread) withObject:nil];
}
- (void)backgroundThread {
    [myClass doLongTask];

    // Done!
    isThreadRunning = NO;
}
- (void)waitForThread {
    if (! isThreadRunning) {
        // Thread completed
        [self doSomething];
    }
}

Как вы хотите обработать ожидание, зависит от вас: возможно, опрос с помощью [NSThread sleepForTimeInterval: 1] или аналогичный, или отправка сообщения себе каждый цикл выполнения.

...