How to wait in NSThread until some event occur in iOS?

前端 未结 3 1041
野性不改
野性不改 2021-02-14 10:10

How to wait inside the NSThread until some event occur in iOS?

eg, We created a NSThread and started a thread loop. Inside the thread loop, there is condition to check w

相关标签:
3条回答
  • 2021-02-14 10:46

    You can use NSCondition. I attach example code "ready-for-test" in a 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];
    }
    
    0 讨论(0)
  • 2021-02-14 10:53

    You can use a semaphore. See the example below, the logic is pretty simply. Im my example, the blocks are executed in background, and my main thread waits for the dispatch signal of the semaphore to go on. The main difference is in my case the thread waiting is the main thread, but the semaphore logic is here, I think you can easily adapt this to your case.

    //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]];
    }
    
    0 讨论(0)
  • 2021-02-14 11:01

    You could use run loop sources. In essence:

    1) On secondary worker thread create and install run loop source, and pass it somehow, along with worker thread run loop reference, to other managing thread which will be sending messages to this one:

        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
    

    Here there are custom routines mentioned above - you are responsible to provide them:

        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) On worker thread, start usual run loop, something similar to this:

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

    3) Now, on managing thread you could signal (send message) to previously received run loop source when needed (and wake up the run loop of those thread in case it is asleep):

        CFRunLoopSourceSignal(runLoopSource);
        CFRunLoopWakeUp(workerThreadRunLoop);
    

    More details are in Apple's guide.

    0 讨论(0)
提交回复
热议问题