One, multithreading security risks

  • Resource sharing
    • A resource may be shared by multiple threads, that is, multiple threads may access the same resource
    • For example, multiple threads access the same object, the same variable, the same file
  • When multiple threads access the same resource, data corruption and data security issues can easily occur

Two, multi-thread security hazard example 01 – money withdrawal money

  • The simulation code is as follows

  • Run the program and the result is as follows

  • Normally, you should5000, take2500So it should be left3500But the result is left2500
  • Run the simulation again

  • You can see there’s only one left2000This is the multi-threaded security problem, is data corruption

Three, multi-thread security risk example 02 – selling tickets

  • Code simulation is as follows

  • Run the program to simulate selling tickets

  • In sales10Zhang, it should be surplus0Zhang, but the result is surplus3Chang, that means the data is out of whack

Fourth, multithreading security hidden danger analysis and solutions

1, multi-thread security risk analysis

2, multi-thread security hidden solution

  • Solution: Use thread synchronization technology (synchronization, or coordinated pacing, in a predetermined order)
  • A common thread synchronization technique is locking

5. Thread synchronization scheme in iOS

  • There are several options for thread locking in iOS
OSSpinLock
os_unfair_lock
pthread_mutex
dispatch_semaphore
dispatch_queue(DISPATCH_QUEUE_SERIAL)
NSLock
NSRecursiveLock
NSCondition
NSConditionLock
@synchronized
Copy the code

Prepare the code

  • The aboveMulti-threaded security example 01 - Saving and withdrawing moneyandMultithreaded security example 02 - selling ticketsThe code is packaged into oneBaseDemoClass, the specific code is shown below

  • inBaseDemoFive methods are exposed, two test calls, and three thread calls
  • createAddLockDemoInherited fromBaseDemo

  • ViewControllerThe code is as follows

OSSpinLock

  • OSSpinLockcalledspinlocks, the thread waiting for the lock will be in busy-wait state, occupying CPU resources

1, solveSaving money to withdraw moneyandSelling ticketsSecurity risks

  • inSaving money to withdraw moneyandSelling ticketsaddOSSpinLock

  • Run the program, click on the screen several times, and you’ll find that the results are correct

2,OSSpinLockIt is no longer secure and priority inversion may occur

  • There may be multiple threads in a program, but only one CPU
  • The CPU assigns resources to threads and lets them execute interspersed, say, with three threadsthread1,thread2andthread3
  • By allocating CPU, letthread1After a period of execution, then letthread2Do it for a while, and then let gothread3Execute for a period of time
  • This gives the illusion that there are multiple threads executing tasks simultaneously
  • Threads have priority
    • If the priority is high, the CPU allocates more resources and has more time to execute
    • If the priority is low, the CPU will allocate less resources and the execution will be slow
  • It is possible that low-priority threads will be locked first, but the CPU will execute more high-priority threads, and deadlock like problems will occur
If thread1 and thread2 are locked by OSSpinLock, thread priority is high and thread2 priority is low. If thread2 is locked first but not unlocked, thread priority is low. The CPU switches to 'thread1' because 'thread1' has a higher priority, the CPU allocates more resources to 'thread1'. If an OSSpinLock is in use for a thread1 thread, it will check whether the OSSpinLock is unlocked. This will cause a deadlock problemCopy the code

Os_unfair_lock (mutex)

  • os_unfair_lockUsed to replace insecure OSSpinLock fromiOS10Support only at the beginning
  • From the underlying call, waitos_unfair_lockLocked threads are dormant, not busy, etc
  • The header file needs to be imported#import <os/lock.h>
/ / initialization
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
If lcok is already in use, false is returned on failure and true is returned on success
os_unfair_lock_trylock(&lock);
/ / lock
os_unfair_lock_lock(&lock);
/ / unlock
os_unfair_lock_unlock(&lock);
Copy the code

To solveSaving money to withdraw moneyandSelling ticketsSecurity risks

  • Add in depositing and withdrawing money and selling ticketsos_unfair_lock

  • Run the program, click on the screen several times, and you’ll find that the results are correct

Nine, the pthread_mutex

  • mutexcalledThe mutex, the thread waiting for the lock will sleep
  • The header file needs to be imported#import <pthread.h>
// Initialize the attribute pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT); // Initialize lock pthread_mutex_t pthread; pthread_mutex_init(&pthread, &attr); Pthread_mutexattr_destroy (&attr); Pthread_mutex_destroy (&pthread);Copy the code
  • The value of the attribute type
#define PTHREAD_MUTEX_NORMAL 0
#define PTHREAD_MUTEX_ERRORCHECK	1
#define PTHREAD_MUTEX_RECURSIVE 2
#define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL
Copy the code

1, solveSaving money to withdraw moneyandSelling ticketsSecurity risks

  • Import header file, create lock, lock unlock

  • Running degree, many times click the screen test, can find the result is correct

2. Recursive locking

  • definePthreadTestClass inherits fromNSObject, includingrecursiveIt’s a recursive method

  • ViewControllerThe code is as follows, click the screen after the callPthreadTesttherecursivemethods

  • Click on the screen and you can see that a deadlock has occurred becauserecursiveIn the callrecursiveAt this time, it has not been unlocked and locks again, so a deadlock occurs

  • Set up thepthreadThe property type at initialization isPTHREAD_MUTEX_RECURSIVESo that thepthreadIt’s just a recursive lock

  • Recursive locking allows the same lock to be locked repeatedly on the same thread, so you can see that the recursive method call succeeds

3, conditions,

  • PthreadTestThe code is as follows

  • ViewControllerThe code is as follows

  • When the screen is clicked, the last element is removed from the array and a new element is added. As you can see in the code, the __remove and __add methods are called using different threads

  • The current requirement is that the delete operation can only be performed if the array is not empty. If it is run directly, __remove may be called before __add, which violates the requirement

  • So, we can use conditions to optimize both methods

  • Create cond

  • whenarray.count == 0When the program enters hibernation, only whenarrayAfter new data is added to the thread, it initiates a signal to wake up the dormant thread

  • Run the program, click the screen, you can see the program enter first__removeMethod, but in__addTo add new elements before removing them

NSLock, NSRecursiveLock, NSCondition, NSConditionLock

  • NSLock,NSRecursiveLock,NSConditionandNSConditionLockIs based onpthreadEncapsulated OC objects

1, NSLock

  • AddLockDemoThe code is as follows, directly usedNSLockTo lock

  • ViewControllerThe method is called when the screen is clicked in

  • Run the program, tap the screen, and see that the result is correct

  • To viewGNUStepIn aboutNSLockThe underlying code can be seenNSLockIs the foundationpthreadencapsulatednormalThe lock

2, NSRecursiveLock

  • PthreadTestThe code is as followsNSRecursiveLockrightRecursive functionLock unlock

  • ViewControllerCalled when the screen is clickedrecursivemethods

  • Run the program, click on the screen, and you can seeRecursive lockingThe results of the

  • To viewGNUStepIn aboutNSRecursiveLockThe underlying code of

3, NSCondition

  • PthreadTestThe code is as followsNSConditionLock unlock

  • ViewControllerCalled when the screen is clickedpthreadTestmethods

  • As you can see, it’s called first__removeMethod, but in__addTo give inarrayAfter the new element is addedDelete an element

  • To viewGNUStepIn aboutNSConditionThe underlying code of

4, NSConditionLock

  • NSConditionLockIs theNSConditionFurther encapsulation of
@interface NSConditionLock : NSObject <NSLocking> {
@private
    void *_priv;
}

// condition is set
- (instancetype)initWithCondition:(NSInteger)condition;

/ / condition
@property (readonly) NSInteger condition;

NSConditionLock can be locked only if the condition in NSConditionLock is equal to the condition passed in
- (void)lockWhenCondition:(NSInteger)condition;
// Try locking
- (BOOL)tryLock;
NSConditionLock can only be locked if the condition in NSConditionLock is equal to the condition passed in
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
// Unlock it and set condition in NSConditionLock
- (void)unlockWithCondition:(NSInteger)condition;
If the lock is already in use, wait until limit is used. If it is out of date, no lock is added
- (BOOL)lockBeforeDate:(NSDate *)limit;
NSConditionLock (condition) conditionlock (condition); NSConditionLock (condition) conditionlock (condition)
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
/ / the name of lock
@property (nullable.copy) NSString *name;

@end
Copy the code
  • You can useNSConditionLockSets the execution order of the threads

  • Run the program and you can see the print order

Eleven, synchronization queue to solve multithreading hidden trouble

  • Using synchronous queues, the code is shown below

  • ViewControllerThe following code

  • Click on the screen to see that the result is correct

Twelve, dispatch_semaphore_t

  • You can usedispatch_semaphore_tSet the semaphore to1, to control that only one thread can execute between consents, the actual code is as follows

  • Run the program and click on the screen to see the correct print

13, @ synchronized

  • @synchronizedIs themutexRecursive lock encapsulation
  • Source code view:objc4In theobjc-sync.mmfile
  • @synchronized(obj)Internally generatesobjCorresponding recursive lock, and then lock, unlock operation

1, solve the security risks of multi-threading

  • use@synchronizedTo lock

  • Execute the code, click the screen, and it looks like this

2. Underlying principle of @synchronized

  • findobjc_sync_enterandobjc_sync_exitTwo functions, one for locking and one for unlocking

  • To viewSyncData

  • Click in and findrecursive_mutex_tt

  • To viewrecursive_mutex_ttAnd you can see that the underlying layer is throughos_unfair_recursive_lockEncapsulation of lock

  • And then check throughobjectGet the lock code

  • findLIST_FOR_OBJ, click to view

  • You can see that through the incomingobject, will get a unique identifier called lock

Performance comparison of iOS thread synchronization schemes

Os_unfair_lock OSSpinLock Dispatch_semaphore pthread_mutex dispatch_queue(DISPATCH_QUEUE_SERIAL) NSLock NSCondition pthread_mutex(recursive) NSRecursiveLock NSConditionLock @synchronizedCopy the code

15. Spin lock and mutex comparison

  • When is it cost-effective to use a spin lock?
    • Threads are expected to wait a short time for locks
    • Locking code (critical sections) is often invoked, but contention rarely occurs
    • CPU resources are not tight
    • Multicore processor
  • When is it cost-effective to use a mutex?
    • The thread is expected to wait a long time for the lock
    • Single-core processor
    • There are IO operations in the critical area
    • Critical section code complex or large loop
    • Critical sections are very competitive