如何等待一个线程在Objective-C中完成

时间:2010-10-07 05:48:52

标签: objective-c multithreading cocoa-touch background nsthread

我正在尝试使用我在某处下载的类中的方法。程序执行继续时,该方法在后台执行。在此方法完成之前,我不想让程序继续执行。我该怎么做?

7 个答案:

答案 0 :(得分:39)

以下是使用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 
}

答案 1 :(得分:9)

使用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.)
}


答案 2 :(得分:6)

我的第一个倾向是不做你的建议。我之前使用的技术是为线程提供一个选择器,用于原始对象(位于主线程上)中的方法。当第二个线程启动时,主线程继续执行,但在显示屏上显示某种忙碌指示符。这允许用户交互在需要时继续。

当第二个线程结束时,就在它关闭之前,它会调用主线程上的选择器。然后,选择器引用的方法从显示中删除忙指示符并告诉主线程更新,获取第二个线程生成的任何数据。

我成功地将这个用于访问Web服务的应用程序(在第二个线程上),然后在返回数据后更新显示而不锁定它。这使用户体验更好。

答案 3 :(得分:5)

对于这种情况,我通常使用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]];
}

答案 4 :(得分:1)

同步多线程的几种技术方法,例如NSConditionLock(互斥锁),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]];    
}

答案 5 :(得分:0)

我建议在你自己的方法中结束对类方法的调用,并在完成后设置一个布尔值。例如:

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]或类似方法进行轮询,或者每个运行循环向自己发送一条消息。

答案 6 :(得分:0)

我的技巧是检查任务是否可以运行(如果后台线程已完成),如果它无法运行,那么我会在延迟后再次尝试使用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
}