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

发布于 2024-09-26 14:25:38 字数 70 浏览 6 评论 0原文

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

I'm trying to use a method from a class I downloaded somewhere. The method executes in the background while program execution continues. I do not want to allow program execution to continue until this method finishes. How do I do that?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

我恋#小黄人 2024-10-03 14:25:38

这是使用 GCD 的另一种方法:



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

- (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 
}

Here's another way to do it using GCD:



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

- (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 
}
愿得七秒忆 2024-10-03 14:25:38

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


Use an NSOperationQueue, like this
(from memory so forgive any minor errors -- you'll get the basic idea):


// 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.)
}


无可置疑 2024-10-03 14:25:38

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

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

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

My first inclination is to not do what you are suggesting. The technique I've used before is to give the thread a selector to a method in the originating object (which is on the main thread). When the second thread is started, the main thread keeps executing, but puts up a busy indicator of some sort on the display. This allows user interaction to continue if required.

When the second thread ends, just before it shuts down, it calls the selector on the main thread. The method that the selector references then removes the busy indicator from the display and tells the main thread to update, picking up whatever data the second thread has generated.

I've used this successfully for an app which accesses a web service (on the second thread) and then updates the display once data is returned without locking it. This makes the user experience much nicer.

孤芳又自赏 2024-10-03 14:25:38

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

For such cases I usually using NSCondition class.

//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]];
}
来世叙缘 2024-10-03 14:25:38

多线程同步的几种技术方式,如NSConditionLock(互斥锁)、NSCondition(信号量)。但都是其他语言的通用编程知识(java.. .) 除了 Objective-c。我更喜欢引入runloop(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]];    
}

Several technical ways to synchronize multi-threads, such as NSConditionLock(mutex-lock), NSCondition(semaphore)。But they are common programming knowledge for other languages(java...) besides objective-c. I prefer to introduce run loop(special in Cocoa) to implement thread join:

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]];    
}
心清如水 2024-10-03 14:25:38

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

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

I'd suggest wrapping up call to the class method in your own method, and set a boolean once it completes. For eg:

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

How you wish to handle waiting is up to you: Perhaps polling with [NSThread sleepForTimeInterval:1] or similar, or sending self a message each run loop.

我的技术是检查任务是否可以运行(如果后台线程已完成),如果无法运行,则我使用 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
}

My technique is to check if the task can run (if the background thread is finished), and if it can't run then I use GCD to try again after a delay:

- (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
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文