This article is mainly a simple arrangement of all kinds of locks, convenient follow-up review, after all, the lock in the actual project with less, and then the knowledge point, old do not remember. A bad pen is better than a good memory. I mainly refer to li Mingjie’s teaching video. If there is any infringement, please contact me and I will delete it immediately. Then attach the author’s link: www.520it.com

Speaking of locks must involve multithreading, so first a brief introduction to iOS multithreading GCD. A few confusing terms:

  • Synchronization: A task is executed in the current thread without the ability to start a new thread.
  • Asynchrony: Perform tasks in a new thread and have the ability to start a new thread;
  • Concurrency: Multiple tasks are executed concurrently (simultaneously).
  • Serial: After one task is executed, the next task is executed.

The execution effect of various queues

Concurrent queue Manually created serial queues The home side column
Sync No new thread is opened. Serial execution of tasks No new thread is opened. Serial execution of tasks No new thread is opened. Serial execution of tasks
Async Start a new thread; Concurrent execution of tasks Start a new thread; Serial execution of tasks No new thread is opened. Serial execution of tasks

Note: Adding tasks to the current serial queue using sync will get stuck in the current serial queue (deadlock).

The use of queue groups

Use GCD to achieve: asynchronous concurrent execution of task 1, task 2. After completing tasks 1 and 2, go back to the main thread and perform task 3

dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_async(group, queue, ^{
        NSLog(@"Mission one.");
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"Mission two.");
    });
    dispatch_group_notify(group, queue, ^{
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"Return to main thread for task three.");
        });
    });
Copy the code

Multithreading security risks:

  • When multiple threads access the same resource, data corruption and data security issues can easily occur.

Thread synchronization scheme in iOS

GNUstep

  • GNUstep is one of the projects of the GNU project, which is a re-open source implementation of the OC library of Cocoa
  • Source address: www.gnustep.org/resources/d…
  • Although GNUstep is not an official source code for Apple, it is a good reference

1. OSSpinLock

  • OSSpinLock is a “spin lock” that uses CPU resources in a busy-wait state.
  • It is no longer secure, and priority inversion may occur;
  • If the thread waiting for the lock has a higher priority, it will always occupy CPU resources, and the thread with a lower priority cannot release the lock.
  • #import
  • Usage:
OSSpinLock lock = OS_SPINLOCK_INIT; // Try locking (if you need to wait, do not lock, return directlyfalse; If there is no need to wait, lock and returntrue) bool result = OSSpinLockTry(&lock); / / lock OSSpinLockLock (& lock); / / unlock OSSpinLockUnlock (& lock);Copy the code

2. os_unfair_lock

  • Os_unfair_lock os_UNfair_lock is used to replace insecure OSSpinLock.
  • Threads waiting for the OS_UNFAIR_LOCK lock are dormant, not busy, etc.
  • #import < OS /lock.h>
  • Usage:
Os_unfair_lock LOCK = OS_UNFAIR_LOCK_INIT; // Try locking (if you need to wait, do not lock, return directlyfalse; If there is no need to wait, lock and returntrue) bool result = os_unfair_lock_trylock(&lock); / / lock os_unfair_lock_lock (& lock); / / unlock os_unfair_lock_unlock (& lock);Copy the code

3. pthread_mutex

  • Introduction: Mutex is called a “mutex”, and the thread waiting for the lock will sleep. cross-platform
  • #import
  • Usage:
// Initialize the lock attribute pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT); // initialize the lock pthread_mutex_t mutex; pthread_mutex_init(&mutex, &attr); // Note that if the attribute value is passed NULL, the default value of the attribute is <! --pthread_mutex_init(&mutex, NULL); Pthread_mutex_trylock (&mutex); / / lock pthread_mutex_lock (& mutex); / / unlock pthread_mutex_unlock (& mutex); Pthread_mutexattr_destroy (&attr); Pthread_mutex_destroy (&mutex);Copy the code

Pthread_mutexes recursive locking

  • When initializing the attribute, pass the parameter PTHREAD_MUTEX_RECURSIVE
  • That is:
// Initialize the lock attribute pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); // initialize the lock pthread_mutex_t mutex; pthread_mutex_init(&mutex, &attr);Copy the code

Lock the pthread_mutex conditions

  • Usage:
// initialize lock NULL to use the default pthread_mutex_t mutex attribute; pthread_mutex_init(&mutex, NULL); Pthread_cond_t condition; pthread_cond_init(&condition, NULL); Pthread_cond_wait (&condition, &mutex); // Activate a thread waiting for the condition pthread_cond_signal(&condition); // Activate all threads waiting for the condition pthread_cond_broadcast(&condition); Pthread_mutex_destroy (&mutex); pthread_cond_destroy(&condition);Copy the code

4. dispatch_semaphore

  • Introduction: Semaphore lock
  • The initial value of a semaphore that can be used to control the maximum number of concurrent accesses by a thread;
  • The initial value of the semaphore is 1, indicating that only one thread is allowed to access resources at the same time to ensure thread synchronization.
  • Simple usage:
// dispatch_semaphoRE_t semaphore = dispatch_semaphore_create(1); // If the semaphore value <= 0, the current thread will sleep and wait (until the semaphore value > 0) // If the semaphore value > 0, then decrement 1, Dispatch_semaphore_wait (semaphore, DISPATCH_TIME_FOREVER); // add 1 dispatch_semaphore_signal(semaphore);Copy the code

5. dispatch_queue(DISPATCH_QUEUE_SERIAL)

  • It is also possible to achieve thread synchronization using serial queues of GCD
dispatch_queue_t queue = dispatch_queue_create("lock_queue", DISPATCH_QUEUE_SERIAL); Dispatch_sync (queue, ^{// task});Copy the code

6. NSLock

  • NSLock is an encapsulation of a mutex lock.
  • Methods the API:
- (void)lock;
- (void)unlock;
- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;
Copy the code

7. NSRecursiveLock

  • NSRecursiveLock encapsulates a mutex recursive lock.
  • The usage is basically similar to NSLock;

8. NSCondition

  • NSCondition is the encapsulation of mutex conditional lock.
  • Methods the API:
- (void)wait;
- (BOOL)waitUntilDate:(NSDate *)limit;
- (void)signal;
- (void)broadcast;
Copy the code

9. NSConditionLock

  • NSConditionLock is a further encapsulation of NSCondition and can set specific conditional values.
  • The main API:
- (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER;

@property (readonly) NSInteger condition;
- (void)lockWhenCondition:(NSInteger)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
Copy the code

10. @synchronized

  • Introduction: @synchronized is the encapsulation of mutex;
  • Objc4 objc-sync.mm file;
  • @synchronized(obj) internally generates a recursive lock corresponding to OBj, and then locks and unlocks the lock;
  • Usage Examples:
@synchronized(obj) {// synchronized(obj)Copy the code

IOS Thread synchronization scheme performance competition (from high to low)

  • os_unfair_lock
  • OSSpinLock
  • dispatch_semaphore
  • pthread_mutex
  • dispatch_queue(DISPATCH_QUEUE_SERIAL)
  • NSLock
  • NSCondition
  • pthread_mutex(recursive)
  • NSRecursiveLock
  • NSConditionLock
  • @synchronized

A spin lock versus a mutex

When is it cost-effective to use spinlocks?

  • The thread is expected to wait for a lock for a short time;
  • 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?

  • Threads are expected to wait a long time for locks.
  • Single-core processor;
  • The critical area has IO operation;
  • Critical section code complex or large loop
  • The critical zone is very competitive;

Note: Apple doesn’t actually recommend using spin locks

atomic

  • Description: Atomic users guarantee atomicity of property setters and getters, which is equivalent to placing thread synchronization locks on getter and setter.
  • See objC4’s objc-accessors.mm file
  • It does not guarantee that the process of using attributes is thread-safe;

Read and write security schemes in iOS

Application Scenarios:

  • Only one thread can write data at a time.
  • Multiple threads are allowed to read data at the same time.
  • Both write and read operations are not allowed at the same time.

The “read multiple write single” is usually used for reading and writing data such as files. There are two schemes in iOS:

  • Pthread_rwlock: read/write lock;
  • Dispatch_barrier_async: asynchronous fence call;

Pthread_rwlock usage:

// Initialize lock pthread_rwlock_t lock; // Initialize lock pthread_rwlock_init(&lock, NULL); // read-lock pthread_rwlock_rdlock(&lock); // read - try to lock pthread_rwlock_tryrdlock(&lock); // write - lock pthread_rwlock_wrlock(&lock); // write - try to lock pthread_rwlock_trywrlock(&lock); / / unlock pthread_rwlock_unlock (& lock); / / destroyed pthread_rwlock_destroy (& lock);Copy the code

dispatch_barrier_async

  • The concurrent queue passed in by this function must be created by itself via dispatch_queue_CREATE
  • If a serial or global concurrent queue is passed in, this function is equivalent to the effect of dispatch_async;
  • Simple usage:
Dispatch_queue_t queue = dispatch_queue_create("rw_queue", DISPATCH_QUEUE_CONCURRENT); Dispatch_async (queue, ^{}); // dispatch_barrier_async(queue, ^{});Copy the code

Finally, thank you li Mingjie, www.520it.com