Thread synchronization lock

Keywords: iOS xcode

1. OSSpinLock High-level lock

OSSpinLock is called a spinlock, and threads waiting for locks are busy-wait, occupying CPU resources all the time.

At present, it is no longer safe, and there may be priority inversion problem.

If the thread waiting for the lock has a higher priority, it will always occupy CPU resources, and the thread with lower priority will not be able to release the lock.

Need to import header file #import <libkern/OSAtomic.h>

#import <libkern/OSAtomic.h>

@interface OSSpinLockDemo()
@property (assign, nonatomic) OSSpinLock lock;
@end

@implementation OSSpinLockDemo

- (void)viewDidLoad {
    [super viewDidLoad];

     // Initialization lock
    self.lock = OS_SPINLOCK_INIT;

    //Lock up
    OSSpinLockLock(&_lock);
    
     //Attempt to lock, return true unless you need to wait, and false unlock if you need to wait
    //bool result = OSSpinLockTry(&_lock);

    [self testSpinLock];

    //Unlock
    OSSpinLockUnlock(&_lock);
}

2. os_unfair_lock Low-level lock

os_unfair_lock is used to replace the unsafe OSSpinLock, which has been supported since iOS 10.

From the bottom invocation point of view, threads waiting for os_unfair_lock lock lock will be dormant, not busy, etc.

Need to import header file #import <os/lock.h>

#import <os/lock.h>

@interface OSUnfairLockDemo()
@property (assign, nonatomic) os_unfair_lock lock;
@end

@implementation OSUnfairLockDemo

- (void)viewDidLoad {
    [super viewDidLoad];

     // Initialization lock
    self.lock = OS_UNFAIR_LOCK_INIT;

    //Lock up
    os_unfair_lock_lock(&_lock);
    
     //Attempt to lock, return true unless you need to wait, and false unlock if you need to wait
    //bool result = os_unfair_lock_trylock(&_lock);

    [self testUnfairLock];

    //Unlock
    os_unfair_lock_unlock(&_lock);
}

ibireme< OSSpinLock is no longer secure > This article indicates that OSSpinLock is no longer secure. In the new version of iOS, the system maintains five different thread priorities / QoS: background, utility, default, user-initiated, user-interactive. High priority threads always execute before low priority threads, and a thread will not be disturbed by lower priority threads. This thread scheduling algorithm can cause potential priority inversion problems, thus destroying spinlock.

3. pthread_mutex - Need to be destroyed

#import <pthread.h>

/*

/*
 * Mutex type attributes
 */
#define PTHREAD_MUTEX_NORMAL		0
#define PTHREAD_MUTEX_ERRORCHECK	1
#define PTHREAD_MUTEX_RECURSIVE		2
#define PTHREAD_MUTEX_DEFAULT		PTHREAD_MUTEX_NORMAL


*/
@interface MutexDemo()
@property (assign, nonatomic) pthread_mutex_t mutex;
@end

@implementation MutexDemo

- (void)viewDidLoad {
    [super viewDidLoad];

    // initiate static
    //pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
    
    // Initialization properties
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
    // Initialization lock
    pthread_mutex_init(_mutex, &attr);

    //pthread_mutex_init(_mutex, NULL); // air transfer is also the default PTHREAD_MUTEX_DEFAULT

    // Destruction attributes
    pthread_mutexattr_destroy(&attr);

    pthread_mutex_lock(&_mutex);
    
    [self testMutex];
    
    pthread_mutex_unlock(&_mutex);
}

- (void)testMutex
{
    NSLog(@"%s", __func__);
}

- (void)dealloc
{
    pthread_mutex_destroy(&_mutex);
}
#import <pthread.h>

@interface MutexDemo()
@property (assign, nonatomic) pthread_mutex_t mutex;
@end

@implementation MutexDemo

- (void)viewDidLoad {
    [super viewDidLoad];

    // Initialization properties
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    // Recursive Locks: Allows the same thread to repeatedly lock a lock
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    // Initialization lock
    pthread_mutex_init(_mutex, &attr);

    // Destruction attributes
    pthread_mutexattr_destroy(&attr);
 
    [self otherTest];
}

- (void)testMutex
{
    NSLog(@"%s", __func__);
}

- (void)otherTest
{
    pthread_mutex_lock(&_mutex);
    
    NSLog(@"%s", __func__);
    
    static int count = 0;
    if (count < 10) {
        count++;
        [self otherTest];
    }
    
    pthread_mutex_unlock(&_mutex);
}

- (void)dealloc
{
    pthread_mutex_destroy(&_mutex);
}
#import <pthread.h>

@interface MutexDemo()
@property (assign, nonatomic) pthread_mutex_t mutex;
@property (assign, nonatomic) pthread_cond_t cond;
@property (strong, nonatomic) NSMutableArray *data;
@end

@implementation MutexDemo

- (void)viewDidLoad {
    [super viewDidLoad];
    // Initialization properties
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);//Conditional lock
    // Initialization lock
    pthread_mutex_init(&_mutex, &attr);
    // Destruction attributes
    pthread_mutexattr_destroy(&attr);
        
    // Initialization conditions
    pthread_cond_init(&_cond, NULL);
        
    self.data = [NSMutableArray array];
    [self otherTest];
}

- (void)otherTest
{
    [[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
}

// Producer-consumer model

// Thread 1
// Delete elements from an array
- (void)__remove
{
    pthread_mutex_lock(&_mutex);
    NSLog(@"__remove - begin");
    
    if (self.data.count == 0) {
        // wait for
        pthread_cond_wait(&_cond, &_mutex);
    }
    
    [self.data removeLastObject];
    NSLog(@"Deleted element");
    
    pthread_mutex_unlock(&_mutex);
}

// Thread 2
// Adding elements to an array
- (void)__add
{
    pthread_mutex_lock(&_mutex);
    
    sleep(1);
    
    [self.data addObject:@"Test"];
    NSLog(@"Adding elements");
    
    // signal
    pthread_cond_signal(&_cond);
    // Radio broadcast
//    pthread_cond_broadcast(&_cond);
    
    pthread_mutex_unlock(&_mutex);
}

- (void)dealloc
{
    pthread_mutex_destroy(&_mutex);
    pthread_cond_destroy(&_cond);
}

4,NSLock

NSLock is the encapsulation of mutex lock

5,NSRecursiveLock

NSRecursiveLock is also an encapsulation of mutex recursive locks. The API is basically the same as NSLock.

6,NSCondition

NSCondition is the encapsulation of mutex and cond

@interface NSConditionDemo()
@property (strong, nonatomic) NSCondition *condition;
@property (strong, nonatomic) NSMutableArray *data;
@end

@implementation NSConditionDemo

- (void)viewDidLoad {
    [super viewDidLoad];

     self.condition = [[NSCondition alloc] init];
     self.data = [NSMutableArray array];
    [self otherTest];
}

- (void)otherTest
{
    [[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
}


// Thread 1
// Delete elements from an array
- (void)__remove
{
    [self.condition lock];
    NSLog(@"__remove - begin");
    
    if (self.data.count == 0) {
        // wait for
        [self.condition wait];
    }
    
    [self.data removeLastObject];
    NSLog(@"Deleted element");
    
    [self.condition unlock];
}

// Thread 2
// Adding elements to an array
- (void)__add
{
    [self.condition lock];
    
    sleep(1);
    
    [self.data addObject:@"Test"];
    NSLog(@"Adding elements");
    
    // signal
    [self.condition signal];
    
    sleep(2);
    
    [self.condition unlock];
}

7,NSConditionLock

NSConditionLock is a further encapsulation of NSConditionLock, which can set specific condition values by default of 0.

@interface NSConditionLockDemo()
@property (strong, nonatomic) NSConditionLock *conditionLock;
@end

@implementation NSConditionLockDemo

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.conditionLock = [[NSConditionLock alloc] initWithCondition:1];

    [self otherTest];
}

- (void)otherTest
{
    [[[NSThread alloc] initWithTarget:self selector:@selector(__one) object:nil] start];
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(__two) object:nil] start];
    
    [[[NSThread alloc] initWithTarget:self selector:@selector(__three) object:nil] start];
}

- (void)__one
{
    [self.conditionLock lock];//Lock directly regardless of condition value
    
    NSLog(@"__one");
    sleep(1);
    
    [self.conditionLock unlockWithCondition:2];
}

- (void)__two
{
    [self.conditionLock lockWhenCondition:2];//The conditions must be met to lock
    
    NSLog(@"__two");
    sleep(1);
    
    [self.conditionLock unlockWithCondition:3];
}

- (void)__three
{
    [self.conditionLock lockWhenCondition:3];
    
    NSLog(@"__three");
    
    [self.conditionLock unlock];
}

8,dispatch_semaphore

Semaphore is called semaphore

The initial value of the semaphore can be used to control the maximum number of concurrent access by threads

The initial value of semaphore is 1, which means that only one thread is allowed to access resources at the same time to ensure thread synchronization.

@interface SemaphoreDemo()
@property (strong, nonatomic) dispatch_semaphore_t semaphore;
@end

@implementation SemaphoreDemo

- (void)viewDidLoad {
    [super viewDidLoad];
     self.semaphore = dispatch_semaphore_create(5);//Setting to 1 is a serial queue and > 1 is concurrent
}

- (void)otherTest
{
    for (int i = 0; i < 20; i++) {
        [[[NSThread alloc] initWithTarget:self selector:@selector(test) object:nil] start];
    }
}

- (void)test
{
    // If the semaphore value > 0, subtract the semaphore value by 1, and then continue executing the code
    // If the value of the semaphore <= 0, it will sleep and wait until the value of the semaphore becomes > 0, let the value of the semaphore be reduced by 1, and then continue to execute the code.
    dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
    
    sleep(2);
    NSLog(@"test - %@", [NSThread currentThread]);
    
    // Let the semaphore value + 1
    dispatch_semaphore_signal(self.semaphore);
}

9. synchronized (recursive lock)

@ synchronized is a kind of encapsulation of mutex recursive lock, which is not prompted when xcode is written, and its performance is relatively low.

@ synchronized(obj) generates a recursive lock corresponding to obj, and then performs locking and unlocking operations

/*
typedef struct alignas(CacheLineSize) SyncData {
    struct SyncData* nextData;
    DisguisedPtr<objc_object> object;
    int32_t threadCount;  // number of THREADS using this block
    recursive_mutex_t mutex;
} SyncData;

*/
//The underlying implementation, objc_sync.mm, objc_sync_enter objc_sync_exit syncData, contains a hash table lookup c++ structure
@synchronized([self class]) {
 
     [self otherTest];
}

Performance ranks from high to low: os_unfair_lock > OSSpinLock > dispatch_semaphore > pthread_mutex > NSLock > NSCondition > pthread_mutex (recursive) > NSConditionLock >@synchronized (time difference calculated on lock and unlock codes) OSSpinLock,dispatch_semaphore performs much better than other locks, but OSSpinLock is superior to OS Lock. Because of the problem of priority inversion, Apple introduced os_unfair_lock as an alternative to iOS 10, and its performance did not decrease that year, but it could not be used after iOS 10 (although spin lock performance is better than mutex lock). It can be seen that @synchronize and NSConditionLock are obviously the worst performers, if the project is particularly sensitive to performance, build It is suggested that dispatch_semaphore be used, and @synchronize can be used if it is convenient, with little impact.

Posted by one on Wed, 21 Aug 2019 06:47:47 -0700