web-dev-qa-db-ja.com

Objective-Cでスレッドが終了するのを待つ方法

どこかでダウンロードしたクラスのメソッドを使おうとしています。プログラムの実行が継続している間、メソッドはバックグラウンドで実行されます。このメソッドが終了するまでプログラムの実行を続行させたくありません。それ、どうやったら出来るの?

14
node ninja

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 
}
39
jpswain

このように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.)
}


9
jpswain

私の最初の傾向は、あなたが提案していることをしないことです。私が以前に使用した手法は、元のオブジェクト(メインスレッド上にある)のメソッドへのセレクターをスレッドに与えることです。 2番目のスレッドが開始されると、メインスレッドは実行を続けますが、ディスプレイにある種のビジーインジケーターを表示します。これにより、必要に応じてユーザーとの対話を続行できます。

2番目のスレッドが終了すると、シャットダウンする直前に、メインスレッドのセレクターが呼び出されます。次に、セレクターが参照するメソッドは、ビジーインジケーターを表示から削除し、メインスレッドに更新するように指示し、2番目のスレッドが生成したデータを取得します。

これは、(2番目のスレッドで)Webサービスにアクセスし、データが返されたときにロックせずに表示を更新するアプリで正常に使用しました。これにより、ユーザーエクスペリエンスが大幅に向上します。

6
drekka

そのような場合、私は通常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
Mike Glukhov

NSConditionLock(mutex-lock)、NSCondition(semaphore)など、マルチスレッドを同期するためのいくつかの技術的な方法。ただし、Objective以外の言語(Java ...)の一般的なプログラミング知識です。 -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]];    
}
1
Richard

私のテクニックは、タスクが実行できるかどうかを確認することです(バックグラウンドスレッドが終了している場合)。実行できない場合は、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
Abhi Beckert

クラスメソッドの呼び出しを独自のメソッドでラップし、完了したらブール値を設定することをお勧めします。例:

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]などでポーリングするか、実行ループごとにメッセージを自分に送信します。

0
v01d