10

如何在 NSThread 中等待,直到 iOS 中发生某些事件?

例如,我们创建了一个 NSThread 并启动了一个线程循环。在线程循环内部,有条件检查消息队列是否有消息。如果有消息,那么它会调用相应的方法来做一些操作,否则它应该等到消息队列被新消息填充。

是否有任何 API 或方法可用于等到某些事件发生?

For Example 

NSThread *thread = [NSThread alloc]....@selector(threadLoop)

- (void)threadLoop
{
   // Expecting some API or method that wait until some messages pushed into the message queue
   if (...) {

   }
}

任何帮助都应该不胜感激。

4

3 回答 3

14

您可以使用 NSCondition。我在 ViewController 中附加示例代码“准备测试”

@interface ViewController ()

@property (strong, nonatomic) NSCondition *condition;
@property (strong, nonatomic) NSThread *aThread;

// use this property to indicate that you want to lock _aThread
@property (nonatomic) BOOL lock;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    // start with the thread locked, update the boolean var
    self.lock = YES;

    // create the NSCondition instance
    self.condition = [[NSCondition alloc]init];

    // create the thread and start
    self.aThread = [[NSThread alloc] initWithTarget:self selector:@selector(threadLoop) object:nil];
    [self.aThread start];

}

-(void)threadLoop
{
    while([[NSThread currentThread] isCancelled] == NO)
    {
        [self.condition lock];
        while(self.lock)
        {
            NSLog(@"Will Wait");
            [self.condition wait];

            // the "did wait" will be printed only when you have signaled the condition change in the sendNewEvent method
            NSLog(@"Did Wait");
        }

        // read your event from your event queue
        ...


        // lock the condition again
        self.lock = YES;
        [self.condition unlock];
    }

}

- (IBAction)sendNewEvent:(id)sender {
    [self.condition lock];
    // put the event in the queue
    ...


    self.lock = NO;
    [self.condition signal];
    [self.condition unlock];
}
于 2013-07-31T15:25:04.693 回答
4

您可以使用运行循环源。在本质上:

1)在辅助工作线程上创建并安装运行循环源,并以某种方式将其与工作线程运行循环引用一起传递给将向该线程发送消息的其他管理线程:

    CFRunLoopSourceContext context = {0, self, NULL, NULL, NULL, NULL, NULL,
                                    &RunLoopSourceScheduleRoutine,
                                    RunLoopSourceCancelRoutine,
                                    RunLoopSourcePerformRoutine};
    CFRunLoopSourceRef runLoopSource = CFRunLoopSourceCreate(NULL, 0, &context);
    CFRunLoopRef runLoop = CFRunLoopGetCurrent();
    CFRunLoopAddSource(runLoop, runLoopSource, kCFRunLoopDefaultMode);
    // Pass runLoopSource and runLoop to managing thread

这里有上面提到的自定义例程 - 您有责任提供它们:

    RunLoopSourceScheduleRoutine - called when you install run loop source (more precisely, when you call CFRunLoopAddSource)

    RunLoopSourceCancelRoutine - called when you remove run loop source (more precisely, when you call CFRunLoopSourceInvalidate)

    RunLoopSourcePerformRoutine - called when run loop source was signaled (received a message from manager thread) and this is a place where you should perform a job

2)在工作线程上,启动通常的运行循环,类似于以下内容:

    BOOL done = NO;
    do {
        int result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, YES);
        done = (result == kCFRunLoopRunStopped) || (result == kCFRunLoopRunFinished);
    } while (!done); 

3)现在,在管理线程时,您可以在需要时向先前接收到的运行循环源发出信号(发送消息)(并唤醒那些线程的运行循环以防它处于睡眠状态):

    CFRunLoopSourceSignal(runLoopSource);
    CFRunLoopWakeUp(workerThreadRunLoop);

更多细节在 Apple 的指南中。

于 2013-07-31T14:13:09.643 回答
2

您可以使用信号量。看下面的例子,逻辑很简单。我的例子,块在后台执行,我的主线程等待信号量的调度信号继续。主要区别是在我的情况下,线程等待是主线程,但是信号量逻辑在这里,我认为您可以轻松地将其适应您的情况。

//create the semaphore
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

[objectManager.HTTPClient deletePath:[address addressURL] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {

      //some code here

        dispatch_semaphore_signal(semaphore);

    }failure:^(AFHTTPRequestOperation *operation, NSError *error) {

       //some other code here

        dispatch_semaphore_signal(semaphore);
    }];

//holds the thread until the dispatch_semaphore_signal(semaphore); is send
while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW))
{
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]];
}
于 2013-07-31T13:33:03.620 回答