Как подождать в NSThread, пока какое-то событие не произойдет в iOS?

Как ждать внутри NSThread, пока какое-то событие не произойдет в iOS?

например, мы создали NSThread и начали цикл потока. Внутри цикла потока есть условие проверить, имеет ли очередь сообщений какие-либо сообщения. Если есть сообщение, то он вызовет соответствующий метод для выполнения какой-либо операции, иначе он должен подождать, пока очередь сообщений не будет заполнена новым сообщением.

Есть ли какой-либо API или методы, доступные для ожидания какого-либо события?

For Example 

NSThread *thread = [NSThread alloc][email protected](threadLoop)

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

   }
}

Любая помощь должна быть оценена.

Ответ 1

Вы можете использовать NSCondition. Я прикрепляю примерный код "ready-for-test" в 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];
}

Ответ 2

Вы можете использовать запустить источники цикла. По существу:

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 guide.

Ответ 3

Вы можете использовать семафор. См. Пример ниже, логика довольно проста. В моем примере блоки выполняются в фоновом режиме, и мой основной поток ожидает, чтобы сигнал отправки семафора продолжался. Главное отличие в моем случае - ожидание потока - это основной поток, но логика семафора здесь, я думаю, вы можете легко адаптировать это к вашему делу.

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