lock
Download the demo text at the bottom
Thread safety solution
Solution uses thread synchronization technology (in a predetermined order)
1, OSSpinLock (Heigh-Leve Lock, spin Lock)
#import <libkern/OSAtomic.h>
OSSpinLock _lock = OS_SPINLOCK_INIT;
OSSpinLockLock(&_lock);
OSSpinLockUnlock(&_lock);
OSSpinLockTry(&_lock); // If the lock cannot be locked, return No
Copy the code
- Because of busy-wait, the CPU is always occupied. Threads do not sleep and wait for the most efficient.
Existing problems
-
Expires after ios10
-
Priority inversion
Because the Lock is a spin Lock, threads do not sleep, so when the lower priority line locks the operation first, the CPU schedules the operation of the higher priority thread, because the lower priority UnLock, then calls the higher priority thread. The higher-priority thread cannot process the operation, and the higher-priority thread keeps calling CPU resources. The system waits for the higher-priority thread to complete its execution before giving resources to the lower-priority thread.
The higher priority thread waits for the lower priority thread Unlock. Low-priority threads wait for CPU resource scheduling. Deadlock causes lock cannot be released. Wait for each other
Os_unfair_lock (low-level Lock, mutex)
import <os.lock.h>
os_unfair_lock _lock = OS_UNFAIR_LOCK_INIT;
os_unfair_lock_lock(&_lock);
os_unfair_lock_unlock(&_lock);
os_unfair_lock_trylock(&_lock);// If the lock cannot be locked, return No
Copy the code
- Supported after ios10
3, ptheard_mutex (low-level Lock, mutex)
#import"pthread.h"
pthread_mutexattr_t attr = {0};
pthread_mutexattr_init(&attr);
PTHREAD_MUTEX_DEFAULT is a common mutex. PTHREAD_MUTEX_RECURSIVE is a recursive lock
// pthread_mutexattr_setType (&attr, PTHREAD_MUTEX_RECURSIVE for recursive locking); / / recursive locking
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
pthread_mutex_init(mutex, &attr);
// Release prevents memory leaks
pthread_mutexattr_destroy(&attr);
Copy the code
1. Recursive lock PTHREAD_MUTEX_RECURSIVE
Recursively locks according to the attr pthread_mutex_parameter
PTHREAD_MUTEX_RECURSIVE: Allows the same thread to be locked repeatedly.
2. Condition
The thread activation signal doesn’t necessarily take place immediately until the last operation, unlock
/ / create a lock
- (void) __initLock:(pthread_mutex_t *) mutex {
pthread_mutexattr_t attr = {0};
pthread_mutexattr_init(&attr);
/ / the mutex
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
pthread_mutex_init(mutex, &attr);
pthread_mutexattr_destroy(&attr);
pthread_condattr_t condAtt={0};
// Create a condition
pthread_cond_init(&_condition, &condAtt);
pthread_condattr_destroy(&condAtt);
}
- (instancetype)init
{
self = [super init];
if (self) {[self __initLock:&_lock];
dispatch_queue_t queue = dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
[self test2];
});
dispatch_async(queue, ^{
sleep(2);
[self test1];
});
}
return self;
}
- (void)test1 {
NSLog(@ "% @"[NSThread currentThread]);
// lock
pthread_mutex_lock(&_lock);
NSLog(@ "1");
// The condition is signaled
pthread_cond_signal( &_condition);
NSLog(@ "3");
//unlock
pthread_mutex_unlock(&_lock);
NSLog(@ "4");
}
- (void)test2 {
NSLog(@ "% @"[NSThread currentThread]);
// lock
pthread_mutex_lock(&_lock);
// Wait for condition to send a signal
pthread_cond_wait(&_condition, &_lock);
NSLog(@ "2");
//unlock
pthread_mutex_unlock(&_lock);
}
Copy the code
The output is
The 2019-09-03 14:55:49. 650729 + 0800 the GCD [1918-36391522] 1 2019-09-03 14:55:49. 650896 + 0800 the GCD [1918-36391522] 3, 2019-09-03 14:55:49.651025+0800 GCD[1918:36391522] 4 2019-09-03 14:55:49.651025+0800 GCD[1918:36391522] 4 2019-09-03 14:55:49.651025+0800 GCD[1918:36391523] 2Copy the code
The above code does not care whether test1 or test2 calls test2 first. Calls that depend on test1 are called only if pthread_cond_signal(&_condition) is executed in test1; In test2, the command is executed as follows:
- Test2
- The lock in the Test2
pthread_mutex_lock(&_lock);
- Wait for condition to send a signal
pthread_cond_wait(&_condition, &_lock);
At this timeunlockLock and threaddormancy - After 2s, Test1 is executed
- The lock in Test1
pthread_mutex_lock(&_lock);
- Output 1
- Condition is signaled
pthread_cond_signal( &_condition);
The test2 thread is activated - The output of 3
- Unlock in Test1
- Test2 Execute the lock operation and continue with output 2
- Test2 unlock
-
8-9 and 10-11 are played simultaneously
-
Condition can send both signal and broadcast pthread_cond_boradcast to activate all pthread_cond_waits
Pay attention to the point
-
If you use condition, do not use the same thread to call wait and signal. If you use condition, the thread will be dormant and cannot execute signal.
-
Structure initialization can only be done directly as shown in the figure below
4, NSLock
NSLock encapsulates the mutex PTHREAD_MUTEX_DEFAULT common lock
- (void)lock;/ / lock
- (void)unlock;/ / unlock
- (BOOL)tryLock;If YES, return YES
- (BOOL)lockBeforeDate:(NSDate *)limit; If YES, return YES
Copy the code
5, NSRecursiveLock
NSLock is an encapsulation of the mutex PTHREAD_MUTEX_RECURSIVE lock
The API is the same as NSLock
- (void)lock;/ / lock
- (void)unlock;/ / unlock
- (BOOL)tryLock;If YES, return YES
- (BOOL)lockBeforeDate:(NSDate *)limit; If YES, return YES
Copy the code
6, NSCondition
NSCondition is the encapsulation of mutex and COND
// The same usage as mutex condtion can refer to the above code
- (void)lock;/ / lock
- (void)unlock;/ / unlock
- (void)wait;/ / wait for
- (BOOL)waitUntilDate:(NSDate *)limit; // How long to wait
- (void)signal; / / semaphore
- (void)broadcast; / / radio
Copy the code
7, NSConditionLock
NSConditionLock is the encapsulation of NSCondition
- (void)lock;/ / lock
- (void)unlock;/ / unlock
- (void)lockWhenCondition:(NSInteger)condition; // Sleep until conditon is activated and locked
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition; // Unlock Changes the value of the condtion
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
Copy the code
8 Dispatch_semaphore.
Dispatch_semaphore controls the number of threads. If the number of Dispatch_semaphore is 1, only one thread can access the Dispatch_semaphore. This ensures thread security
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
// If the semaphore <=0, the current thread will go to sleep until the semaphore value >0
// If the signal value is >0 then -1 and execute the following code
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
[super __saleTicket];
// Semaphore +1
dispatch_semaphore_signal(_semaphore);
Copy the code
9, dispatch_queue (DISPATCH_QUEUE_SERIAL)
Serial queue, when executed in a serial queue, only one thread, to ensure thread safety
10, @ synchronized
Before: Encapsulation of mutex recursive locks
Now: OS_UNFAIR_RECURSIVE_LOCK encapsulation
First, open breakpoint assembly mode
Obj4 source objc_sync.mm
Underlying storage Hash table key: object to be passed in
class recursive_mutex_tt : nocopy_t{ os_unfair_recursive_lock mLock; . }using recursive_mutex_t = recursive_mutex_tt<LOCKDEBUG>;
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;
int objc_sync_enter(id obj)
{
int result = OBJC_SYNC_SUCCESS;
if (obj) {
SyncData* data = id2data(obj, ACQUIRE);
assert(data);
data->mutex.lock();
} else {
// @synchronized(nil) does nothing
if (DebugNilSync) {
_objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug");
}
objc_sync_nil();
}
return result;
}
Copy the code
int objc_sync_exit(id obj)
{
int result = OBJC_SYNC_SUCCESS;
if (obj) {
SyncData* data = id2data(obj, RELEASE);
if(! data) { result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR; }else {
bool okay = data->mutex.tryUnlock();
if(! okay) { result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR; }}}else {
// @synchronized(nil) does nothing
}
return result;
}
Copy the code
Demo download stamp me
As for the time, each time the test is not quite the same, we measure it ~
Interested can pay attention to my public number. It will be updated every day. Thank you very much. Mutual exchange of promotion technology ~