Как подождать окончания потока в Objective-C

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

Ответ 1

Здесь другой способ сделать это с помощью 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 
}

Ответ 2

Использовать 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.)
}


Ответ 3

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

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

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

Ответ 4

В таких случаях я обычно использую класс 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]];
}

Ответ 5

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

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

Ответ 6

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

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] или аналогичный, или отправление сообщения о себе в каждый цикл цикла.

Ответ 7

Моя техника - проверить, может ли задача выполняться (если фоновый поток завершен), и если она не может быть выполнена, я использую 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
}