Multithreading of IOS underlying exploration (XVI) — lock analysis (nslock, nscondition, nsrecursivelock, nscondition)

Time:2022-5-21

How much do you know about IOS development and various locks? NSLock、NSCondtion、NSRecursiveLock…….

review

BeforeA blogThe types of locks are introduced inLastIn the blog@synchronizedLock source code analysis, there are some other locks not introduced, so this blog will analyze some other locks!

Multithreading of IOS underlying exploration (XVI) -- lock analysis (nslock, nscondition, nsrecursivelock, nscondition)

How much do you know about locks?

Multithreading of IOS underlying exploration (I) – process and thread

Multithreading of IOS underlying exploration (II) – threads and locks

Multithreading of IOS bottom layer exploration (3) – getting to know GCD

Multithreading of IOS underlying exploration (IV) – queue of GCD

Multithreading in IOS bottom layer exploration (5) — Analysis of different queue source codes of GCD

Multithreading of IOS bottom exploration (6) – GCD source code analysis (sync synchronous function, async asynchronous function)

Multithreading of IOS underlying exploration (VII) – GCD source code analysis (causes of deadlock)

Multithreading of IOS underlying exploration (8) – GCD source code analysis (function synchronization, asynchrony, single example)

Multithreading of IOS underlying exploration (IX) – GCD source code analysis (fence function)

Multithreading of IOS underlying exploration (10) – GCD source code analysis (semaphore)

Multithreading of IOS underlying exploration (11) – GCD source code analysis (scheduling group)

Multithreading of IOS underlying exploration (12) – GCD source code analysis (event source)

Multithreading of IOS underlying exploration (13) – what do you know about the types of locks?

Multithreading of IOS underlying exploration (14) – how much do you know about @ synchronized locks?

Multithreading of IOS bottom layer exploration (15) – @ synchronized source code analysis

1. Introduction to lock

1.1 classification of locks

  • ⾃ rotary lock: the thread repeatedly checks whether the lock variable can be ⽤. Because the thread keeps executing during this process, it isBusy waiting。 Once you get it⾃ rotary lockThe thread lock will be released directly.⾃ rotary lockIt avoids the scheduling overhead of the process up and down, so it is effective when the thread will only block for a short time.
  • mutex : it is used in multithreaded programming to prevent two threads from entering the same common resources (such as global variables) at the same timeReading and writingMechanism. This is achieved by cutting the code into critical areasmutexThe role of.

There are:NSLock 、pthread_ Mutex, @ synchronized, etc

1.2 classification of locks

  • Conditional lock: is a condition variable. When some resource requirements of the process are not satisfied, it will go to sleep, that is, it is locked. When resources are allocated, the conditional lock is opened and the process continues to run, such as:NSConditionNSConditionLock
  • Recursive lock: that is, the same thread can be locked n times without causing deadlock, such as:NSRecursiveLockpthread_mutex(recursive)
  • Semaphore: it is a more advanced synchronization mechanism. Mutex lock can be said to besemaphoreValue only in0/1Special case of. Semaphores can have more value space to achieve more complexsynchronization, ⽽ not just between threadsmutex, such as:dispatch_semaphore

The classification of locks actually includes three basic types of locks:⾃ rotary lock mutex Read write lock, others ⽐ such asConditional lockRecursive lockSemaphoreAre the packaging and implementation of the upper layer!

  • Read write lock: a read-write lock is actually a special mutex lock. It divides visitors to shared resources into readers and writers. Readers only read and access shared resources, while writers need to write to shared resources.

2. NSLock

  1. Example 1

There are the following codes:

- (void)is_crash{
    NSLog(@"reno");
    for (int i = 0; i < 10000; i++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            _testArray = [NSMutableArray array];
        });
    }
}

  • Run without lock
Multithreading of IOS underlying exploration (XVI) -- lock analysis (nslock, nscondition, nsrecursivelock, nscondition)

Multithreaded access

Without locking, multi-threaded access directly crashed. Now go to lock and see the result?

  • Lock
Multithreading of IOS underlying exploration (XVI) -- lock analysis (nslock, nscondition, nsrecursivelock, nscondition)

NSLock

In the case of locking, it will not break down and ensure the safety of threads.

  1. Example 2
NSLog(@"jpreno");
dispatch_async(dispatch_get_global_queue(0, 0), ^{
            static void (^testMethod)(int);
            testMethod = ^(int value){

                    if (value > 0) {
                        NSLog(@"current value = %d",value);
                        testMethod(value - 1);
                    }
            };
            testMethod(10);
        });
  • Print results
Multithreading of IOS underlying exploration (XVI) -- lock analysis (nslock, nscondition, nsrecursivelock, nscondition)

Operation results

If there is no problem with the print result, add oneforWhat about the cycle?

Multithreading of IOS underlying exploration (XVI) -- lock analysis (nslock, nscondition, nsrecursivelock, nscondition)

Operation results

Now, the printed data is confused, that is, multi-threaded access. The solution isLockSo where is it? Most people will add here as follows:

Multithreading of IOS underlying exploration (XVI) -- lock analysis (nslock, nscondition, nsrecursivelock, nscondition)

code

Will it print normally if it is added in the figure above? We don’t know yet. Now let’s run the code and have a look!

Multithreading of IOS underlying exploration (XVI) -- lock analysis (nslock, nscondition, nsrecursivelock, nscondition)

Operation results

From the running print results, the data is still disordered, which is obviousNSLockThe position of the lock is not correct, so it is correct ✅ Where is the locking position of the? See:

Multithreading of IOS underlying exploration (XVI) -- lock analysis (nslock, nscondition, nsrecursivelock, nscondition)

Lock and print correctly

The problem can be solved only by adding the lock in the position shown in the figure, or directlytestMethod(10)This place is also OK.

[jp_lock lock];// Lock
testMethod(10);
[jp_lock unlock];// Unlock

Generally, we like to write it together with the business code, as follows:

Multithreading of IOS underlying exploration (XVI) -- lock analysis (nslock, nscondition, nsrecursivelock, nscondition)

Code running results

Here, it has been recursive and locked without unlocking, which is equivalent to deadlock, but the program hasn’t crashed yet. So why? Nslock does not support recursive locking and is not recursive.

3. NSRecursiveLock

We remember a lock——NSRecursiveLock, the performance of this lock is also good, and it supports recursion. It is used as follows:

Multithreading of IOS underlying exploration (XVI) -- lock analysis (nslock, nscondition, nsrecursivelock, nscondition)

Print results

NSRecursiveLockAlthough it has recursion, it does not support multi-threaded recursion. It crashes after running only once. So at this time, some pretty people must think of using it@synchronizedThis lock, yes, this lock conforms to the characteristics of recursion and multithreading.

Multithreading of IOS underlying exploration (XVI) -- lock analysis (nslock, nscondition, nsrecursivelock, nscondition)

Operation results

By adding@synchronizedThis lock solves the problem perfectly,NSRecursiveLockYes, it’s solvedNSLockImpossibleRecursion, used here@synchronizedYes, it’s solvedNSRecursiveLockmust notMultithreading

4. NSCondition

NSConditionThe object of is actually used as ⼀ locks and ⼀ thread inspectors.

  • Lock is mainly used to protect the data source when detecting conditions and perform the tasks caused by conditions;
  • The Thread Checker mainly determines whether to continue running the thread according to the conditions, that is, whether the thread is blocked.

1:[condition lock]: generally, multiple threads can access and modify the same data source at the same time, so as to ensure that it is in the same place
The data source is only accessed and modified ⼀ times within the time, and the commands of other threads need to wait outside the lock until
Unlock to access
2:[condition unlock]: andlockAt the same time
3:[condition wait]: keep the current thread waiting
4:[condition signal]:CPUSignal that the thread is not waiting and can continue to execute.

Now take an example of producers and consumers. The code is as follows:

- (void)viewDidLoad {
    [super viewDidLoad];

    self.ticketCount = 0;
     _testCondition = [[NSCondition alloc] init];
    [self jp_testConditon];

}

#pragma mark -- NSCondition

- (void)jp_testConditon{
    
    //Create production consumer
    for (int i = 0; i < 50; i++) {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            [self jp_producer];
        });
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            [self jp_consumer];
        });
        
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            [self jp_consumer];
        });
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
            [self jp_producer];
        });
    }
}

- (void)jp_producer{
    self.ticketCount = self.ticketCount + 1;
    Nslog (@ "produce an existing count%zd", self.ticketcount);
}

- (void)jp_consumer{
 
    if (self.ticketCount == 0) {
        Nslog (@ "waiting count% ZD", self.ticketcount);
    }
    self.ticketCount -= 1;
    Nslog (@ "count% ZD left after consumption", self.ticketcount);
}

The operation results are as follows:

Multithreading of IOS underlying exploration (XVI) -- lock analysis (nslock, nscondition, nsrecursivelock, nscondition)

Examples of producers and consumers

From the running results, we can see that there is a negative number. You have consumed all the things produced by our producer and have no more. If you are still consuming, there is an accident of thread unsafe access.

So we have toensureProduction line and consumption lineData security, you need toLockProcessing to ensure multithreading safety, but this is only their internal, but there are differences between themConsumption relationshipFor example, if the inventory of production is gone, the consumer shall not be notifiedwait for, after productionnoticeConsumers come and pay.

Now the locking transformation is carried out as follows:

(void)jp_producer{
    [_testCondition lock]; //  Multithreading impact of operations
    self.ticketCount = self.ticketCount + 1;
    Nslog (@ "produce an existing count%zd", self.ticketcount);
    [_testCondition signal]; //  Send a signal to inform consumers that I have finished production here and you can come to consume
    [_testCondition unlock];
}

- (void)jp_consumer{
 
     [_testCondition lock];  //  Multithreading impact of operations
    if (self.ticketCount == 0) {
        Nslog (@ "waiting count% ZD", self.ticketcount);
        [_testCondition wait];//  Waiting for the producer to produce something
    }
    //Pay attention to consumption behavior after waiting for the judgment of conditions
    self.ticketCount -= 1;
    Nslog (@ "count% ZD left after consumption", self.ticketcount);
     [_testCondition unlock];
}

Now let’s see if the result after locking is safe? As follows:

Multithreading of IOS underlying exploration (XVI) -- lock analysis (nslock, nscondition, nsrecursivelock, nscondition)

Insert picture description here

Obviously, the printing after locking is normal, there is no negative number, and the data is safe!

  • If the product is insufficient[_testCondition wait]Wait to make consumers stop spending
  • use[_testCondition signal]The simulation now has production and can be consumed. Send a signal to the waiting thread to notify consumption

4. Summary

  • For multi-threaded access, you need to ensure the security of data, and you can continue to lock processing
  • NSLockRecursive locking is not supported
  • NSRecursiveLockAlthough it is recursive, it has no multithreading feature
  • NSConditionThe object of is actually used as ⼀ locks and ⼀ thread inspectors

More content is constantly updated

Just like it

If you think you have something to gain, you can have a wave, collect + pay attention, comment + forward, so as not to find me next time

Welcome to exchange messages, criticize and correct, learn from each other and improve yourself