This is the 10th day of my participation in the August Revision Challenge. In the last article we introduced the principle of @synchoroized and how to use it. Let’s move on to the other locks.

1.NSLock

NSLockIs the OC layer encapsulation, the bottom layer is rightpthread_mutexSee a daily multithreaded operation on data causing unsafe crashes, asynchronous functions in multiple threads on an array of old valuesreleaseThe new valueretain. Because it’s asynchronous at some point, multiple threads are working on the array at the same timereleaseOperation, missedretain, causing the wild pointer to crash.

After locking, security is ensured against collapse.

To viewNSLockThe header file

Is in theFoundationFramework,NSObjectFollow theNSLockingThe agreement. As a result of the OCFoundationThe framework is not open source, so withSwiftOpen source frameworkFoundationTo analyze theNSLockThe underlying implementation of OC is similar to OC.

pthread_mutexInitialization does something aboutpthread_mutexInitialization of the property of. Lock and unlock is rightpthread_mutexOperation of a mutex. Swift supportwindowsIn the future, it seems to want to become a cross-platform language.

  • Recursive operation analysis

It works fine. We can do it in multiple threadsout-of-order

We reported an error in recursively locking multiple threads

Let’s change it to NSRecursiveLock recursive lock

Although we can lock the whole recursion area can also achieve the unified effect, but the meaning is different, just for a certain code block under multithreading mutually exclusive.

We’re using what we use every day@SynchorizedSupport for multithreading and recursion.

2. NSRecursiveLock

Above we know that NSRecursiveLock is used to solve asynchronous recursion cases, let’s look at the source code

And basicallyNSLockBoth are truepthread_mutexThe difference between encapsulation and operation is

NSLock: NSRecursiveLock:Sets the type toPTHREAD_MUTEX_RECURSIVE, thus supporting recursion.

3. NSCondition

The NSCondition object actually acts as a lock and a thread inspector. The lock is used to protect the data source and perform the tasks triggered by the condition when the condition is detected. The thread checker mainly decides whether to continue running a thread based on conditions, that is, whether the thread is blocked

1:[condition lock];// Multiple threads can access and modify the same data source at the same time. Ensure that the data source can be accessed and modified only once at the same time. Commands of other threads must wait outside the LOCK until the unlock is accessed

2:[condition unlock];// use \ with lock
3:[condition wait];// leave the current thread in the wait state
4:[condition signal];// The CPU signals the thread to stop waiting and continue executing
Copy the code

The classic model is the production of consumer model than we buy and sell tickets, only one person can operate at the same time, others buy tickets and have to wait for the purchase.

Take a look at the source:Source code is also based onpthread_mutexEncapsulation and operation, similar to NSLock, with a condition and wait.waitoperationBlocking threadsAnd make it enterA dormant state;signalOperation isWake up theA thread that is dormant waiting;broadcastWakes up all waiting threads.

4. NSConditionLock

NSConditionLock is a conditional lock, and once one thread has acquired the lock, the other threads must wait

/ / initialization
NSConditionLock *conditionLock = [[NSConditionLock alloc] initWithCondition:2];

/ / said conditionLock expects the lock, and if there are no other thread lock (no need to judge the internal condition) of the following code, it can perform visit if you have other thread lock (lock may be conditions, or unconditional lock), the waiting, until the other threads to unlock
[conditionLock lock]; 

// Indicates that if no other thread has acquired the lock, but the condition inside the lock is not equal to condition A, it still cannot acquire the lock and is still waiting. If the internal condition is equal to the condition A, and no other thread acquires the lock, the code area is entered, while it is set to acquire the lock, and any other thread will wait for its code to complete until it unlocks.ConditionLock lockWhenCondition:A condition;// Release the lock and set the internal condition to AConditionLock unlockWithCondition:A condition.// indicates that if the thread is locked (the lock is not acquired) and the time is exceeded, the thread is no longer blocked. Note, however, that the value returned is NO, which does not change the state of the lock. The purpose of this function is to allow processing in both states
return= [conditionLock lockWhenCondition:A conditional beforeDate:A time];// Condition is an integer, which is used to compare conditions internally


Copy the code

Thread 1 has the highest priority, followed by thread 3, followed by thread 2. So the order of execution isThe 1-3-2, but thread 1 sets the condition onlyConditionis1When we do it. WeThe default is 3, so don’t go, thread 3There are no conditions, normal lock unlock, afterThread 2The sign condition is executed for 3, finallyUnlock condition changed to 1Thread 1 performs the task.

Let’s switch to conditional thread 2 sign conditional execution.

Source andNSConditionSimilarly, inNSConditionOn the basis of setting conditionscondition, can be freely matched more flexible.

5. atomic

Atomic locks we often encounter, after the addition of a spin lock thread is held, in a busy state, will block the thread. However, we use nonatomic properties for attribute modification, which is the system’s own spin lock. Setter methods call different methods based on the modifiers, which eventually call the reallySetProperty method, which has both atomic and non-atomic operations

reallySetProperty: os_unfair_lock:useos_unfair_lockLock and unlock

getterMethods onlyatomicIs handled similarly:

6. pthread_mutex

The pthread_mutex is the mutex itself. When the lock is occupied and other threads apply for the lock, instead of waiting, the thread blocks and sleeps

// Import the header file
#import <pthread.h>

// Declare mutex globally
pthread_mutex_t _lock;

// Initialize the mutex
pthread_mutex_init(&_lock, NULL);

/ / lock
pthread_mutex_lock(&_lock);
Thread-safe operations are required here
/ / unlock
pthread_mutex_unlock(&_lock);

/ / releases the lock
pthread_mutex_destroy(&_lock);

Copy the code

7. To read and write locks

As the name implies, when we operate on data, multiple threads can read it, but when we write it, there is only one operation. Read/write locks are suitable for situations where data structures are read much more often than written. The read-write lock is also called shared-exclusive lock because it can be shared while the write lock means exclusive lock. We mainly meet the following points:

  1. It can be read by multiple threads
  2. While writing, only a single thread writes, while other write and read operations wait.
  3. While reading, any write in progress is read after the write is complete.
  4. Multiple read processes are read in sequence to prevent different order

Simulate with dispatch_barrier_async:

#import "TestViewController.h"



@interface TestViewController ()

///<#name#>

@property (nonatomic, strong) dispatch_queue_t  queue;

@property (nonatomic, strong) dispatch_queue_t  queue2;

///

@property (nonatomic, strong) NSArray *data;

@end

@implementation TestViewController


- (void)viewDidLoad {

    [super viewDidLoad];

    // Do any additional setup after loading the view.

    

    

    self.queue = dispatch_queue_create("customQueue", DISPATCH_QUEUE_CONCURRENT);

    self.queue2 = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);

    // assign - write

    for (int i =0; i<100; i++) {

        

        dispatch_async(self.queue, ^{

           

            [self writeAction:i];

            

            

        });

        

    }

    

    // Value - Read

    for (int i =0; i<100; i++) {

        

        dispatch_async(self.queue, ^{

           

                [self readAction];

            

        });

        

    }

    

}

-(void)readAction

{

    dispatch_async(self.queue2, ^{

        

        NSLog(@"Number of groups in reading %ld",self.data.count); }); } - (void)writeAction:(NSInteger)i

{

        dispatch_barrier_async(self.queue, ^{

            

            if (i%9= =0) {

                self.data = @[@"2"The @"wq"The @"wq"];

            }else{

                

                self.data = nil;

            }

            NSLog(@"Number of groups in writing ****** %ld",self.data.count);

        });

    

}

Copy the code

dispatch_barrier_asyncIt has been discussed before that the task of the fence function must wait until the previous task in the thread is completed. In the process of reading, the task is read in the serial queue to prevent different order of data reading.

8. To summarize

Locks are mainly designed for thread safety. Different locks have different performance and corresponding scenarios. Above we understand the general principle and use of several locks, according to the development of the scene to choose the appropriate lock, in some simple recursive multithreading situation using @synchorized is better, in the real machine has also been optimized.