How do I use NSConditionLock? Or NSCondition

こ雲淡風輕ζ 提交于 2019-11-27 10:48:44

EDIT: as @Bonshington commented, this answer refers to NSCondition (as opposed to NSConditionLock):

- (void) method1 {

    [myCondition lock];
    while (!someCheckIsTrue)
        [myCondition wait];


    // Do something.


    [myCondition unlock];
}

- (void) method2 {

    [myCondition lock];


    // Do something.


    someCheckIsTrue = YES;
    [myCondition signal];
    [myCondition unlock];
}

The someCheckIsTrue can be anything, it could be a simple BOOL variable or even something like [myArray count] == 0 && color == kColorRed, it doesn't matter. It only matters that in one method you check for a condition while you have the lock and in another method you make changes that can make the condition become true also while having the lock. The magic is the wait and signal part: the wait actually unlocks the lock and reacquires it after some other thread called signal.

For those that want a sample test class here I post what I did to play around and understand how NSCondition works.

// --- MyTestClass.h File --- //
@interface MyTestClass

- (void)startTest;

@end

// --- MyTestClass.m File --- //
@implementation MyTestClass
{
    NSCondition *_myCondition;
    BOOL _someCheckIsTrue;
}

- (id)init
{
    self = [super init];
    if (self) 
    {
        _someCheckIsTrue = NO;
        _myCondition = [[NSCondition alloc] init];
    }
    return self;
}

#pragma mark Public Methods

- (void)startTest
{
    [self performSelectorInBackground:@selector(_method1) withObject:nil];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(5);
        [self performSelectorInBackground:@selector(_method2) withObject:nil];
    });
}

#pragma mark Private Methods

- (void)_method1
{
    NSLog(@"STARTING METHOD 1");

    NSLog(@"WILL LOCK METHOD 1");
    [_myCondition lock];
    NSLog(@"DID LOCK METHOD 1");

    while (!_someCheckIsTrue)
    {
        NSLog(@"WILL WAIT METHOD 1");
        [_myCondition wait];
        NSLog(@"DID WAIT METHOD 1");
    }

    NSLog(@"WILL UNLOCK METHOD 1");
    [_myCondition unlock];
    NSLog(@"DID UNLOCK METHOD 1");

    NSLog(@"ENDING METHOD 1");
}

- (void)_method2
{
    NSLog(@"STARTING METHOD 2");

    NSLog(@"WILL LOCK METHOD 2");
    [_myCondition lock];
    NSLog(@"DID LOCK METHOD 2");

    _someCheckIsTrue = YES;

    NSLog(@"WILL SIGNAL METHOD 2");
    [_myCondition signal];
    NSLog(@"DID SIGNAL METHOD 2");

    NSLog(@"WILL UNLOCK METHOD 2");
    [_myCondition unlock];
    NSLog(@"DID UNLOCK METHOD 2");
}

@end


// --- Output --- //
/*
2012-11-14 11:01:21.416 MyApp[8375:3907] STARTING METHOD 1
2012-11-14 11:01:21.418 MyApp[8375:3907] WILL LOCK METHOD 1
2012-11-14 11:01:21.419 MyApp[8375:3907] DID LOCK METHOD 1
2012-11-14 11:01:21.421 MyApp[8375:3907] WILL WAIT METHOD 1
2012-11-14 11:01:26.418 MyApp[8375:4807] STARTING METHOD 2
2012-11-14 11:01:26.419 MyApp[8375:4807] WILL LOCK METHOD 2
2012-11-14 11:01:26.419 MyApp[8375:4807] DID LOCK METHOD 2
2012-11-14 11:01:26.420 MyApp[8375:4807] WILL SIGNAL METHOD 2
2012-11-14 11:01:26.420 MyApp[8375:4807] DID SIGNAL METHOD 2
2012-11-14 11:01:26.421 MyApp[8375:4807] WILL UNLOCK METHOD 2
2012-11-14 11:01:26.421 MyApp[8375:3907] DID WAIT METHOD 1
2012-11-14 11:01:26.421 MyApp[8375:4807] DID UNLOCK METHOD 2
2012-11-14 11:01:26.422 MyApp[8375:3907] WILL UNLOCK METHOD 1
2012-11-14 11:01:26.423 MyApp[8375:3907] DID UNLOCK METHOD 1
2012-11-14 11:01:26.423 MyApp[8375:3907] ENDING METHOD 1
*/

NSConditionLock example program.

#import <Foundation/Foundation.h>

#define IDLE 0
#define START 1
#define TASK_1_FINISHED 2
#define TASK_2_FINISHED 3
#define CLEANUP_FINISHED 4

#define SHARED_DATA_LENGTH 1024 * 1024 * 1024

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:IDLE];
        char *shared_data = calloc(SHARED_DATA_LENGTH, sizeof(char));

        [NSThread detachNewThreadWithBlock:^{
            [lock lockWhenCondition:START];

            NSLog(@"[Thread-1]: Task 1 started...");
            for (size_t i = 0; i < SHARED_DATA_LENGTH; i++) {
                shared_data[i] = 0x00;
            }
            [lock unlockWithCondition:TASK_1_FINISHED];
        }];

        [NSThread detachNewThreadWithBlock:^{
            [lock lockWhenCondition:TASK_1_FINISHED];
            NSLog(@"[Thread-2]: Task 2 started...");
            for (size_t i = 0; i < SHARED_DATA_LENGTH; i++) {
                char c = shared_data[i];
                shared_data[i] = ~c;
            }
            [lock unlockWithCondition:TASK_2_FINISHED];
        }];

        [NSThread detachNewThreadWithBlock:^{
            [lock lockWhenCondition:TASK_2_FINISHED];

            NSLog(@"[Thread-3]: Cleaning up...");
            free(shared_data);
            [lock unlockWithCondition:CLEANUP_FINISHED];
        }];

        NSLog(@"[Thread-main]: Threads set up. Waiting for 2 task to finish");
        [lock unlockWithCondition:START];
        [lock lockWhenCondition:CLEANUP_FINISHED];
        NSLog(@"[Thread-main]: Completed");
    }
    return 0;
}

Swift 5 version of @GRiMe2D answer from Playground:

let myCondition = NSCondition()

var someCheckIsTrue = false

func method1() {
    print("STARTING METHOD 1")

    print("WILL LOCK METHOD 1")
    myCondition.lock()
    print("DID LOCK METHOD 1")

    while (!someCheckIsTrue) {
        print("WILL WAIT METHOD 1")
        myCondition.wait()
        print("DID WAIT METHOD 1")
    }

    print("WILL UNLOCK METHOD 1")
    myCondition.unlock()
    print("DID UNLOCK METHOD 1")

    print("ENDING METHOD 1")
}

func method2() {
    print("STARTING METHOD 2")

    print("WILL LOCK METHOD 2")
    myCondition.lock()
    print("DID LOCK METHOD 2")

    someCheckIsTrue = true

    print("WILL SIGNAL METHOD 2")
    myCondition.signal()
    print("DID SIGNAL METHOD 2")

    print("WILL UNLOCK METHOD 2")
    myCondition.unlock()
    print("DID UNLOCK METHOD 2")

    print("ENDING METHOD 2")
}

DispatchQueue.global().async {
    method1()
}

DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + 0.5) {
    method2()
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!