How can I ensure thread security in iOS-multithreading? ios-Multithreading

Source: Internet
Author: User

How can I ensure thread security in iOS-multithreading? ios-Multithreading
I. Preface

Some time ago I read several open-source projects and found that they have different ways to maintain thread synchronization.@ Synchronized, NSLock, dispatch_semaphore, NSCondition, pthread_mutex, OSSpinLock. I checked them online and found that their implementation mechanisms are different, and their performance is also different.

Sorry, @ synchronized, which is the most commonly used, has the worst performance.

 

II. Introduction and use of 2.1, @ synchronized
NSObject * obj = [[NSObject alloc] init]; dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {@ synchronized (obj) {NSLog (@ "start 1 of operations requiring thread synchronization"); sleep (3); NSLog (@ "End 1 of operations requiring thread synchronization ");}}); dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {sleep (1); @ synchronized (obj) {NSLog (@ "operations requiring thread synchronization 2 ");}});

@ Synchronized (obj) The obj used by the command is the unique identifier of the lock. It is mutually exclusive only when the identifiers are the same. If @ synchronized (obj) in thread 2) changed to @ synchronized (self), so thread 2 will not be blocked. The advantage of implementing the lock by @ synchronized command is that we do not need to explicitly create lock objects in the code, the lock mechanism can be implemented, but as a precaution, @ synchronized implicitly adds an exception handling routine to protect the code, this processing routine Automatically releases the mutex when an exception is thrown. So if you don't want the implicit exception handling routine to bring additional overhead, you can consider using the Lock Object.

The preceding result is as follows:

20:48:35. 747 SafeMultiThread [35945: 580107] start of Operation 1 requiring Thread Synchronization
20:48:38. 748 SafeMultiThread [35945: 580107] End of operation 1 requiring Thread Synchronization
20:48:38. 749 SafeMultiThread [35945: 580118] operations that require thread synchronization 2

 

2.2. dispatch_semaphore
Required signal = second (1); dispatch_time_t overTime = dispatch_time (DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC); dispatch_async (second, 0), ^ {second (signal, overTime ); NSLog (@ "start 1 of operations requiring thread synchronization"); sleep (2); NSLog (@ "End 1 of operations requiring thread synchronization "); dispatch_semaphore_signal (signal) ;}); dispatch_async (dispatch_get_global_queue (queue, 0), ^ {sleep (1); dispatch_semaphore_wait (signal, overTime ); NSLog (@ "operations that require thread synchronization 2"); dispatch_semaphore_signal (signal );});

 

Dispatch_semaphore is a method used by GCD for synchronization. There are three related functions: dispatch_semaphore_create, dispatch_semaphore_signal, and dispatch_semaphore_wait.

(1) The dispatch_semaphore_create statement is as follows:

Dispatch_semaphore_t dispatch_semaphore_create (long value );

If the input parameter is long, a semaphore of the dispatch_semaphore_t type and the value is output.

It is worth noting that the input parameter value must be greater than or equal to 0. Otherwise, dispatch_semaphore_create returns NULL.

(2) dispatch_semaphore_signal declaration:

Long dispatch_semaphore_signal (dispatch_semaphore_t dsema)

This function will add 1 to the value of the input semaphore dsema;

(3) dispatch_semaphore_wait statement:

Long dispatch_semaphore_wait (dispatch_semaphore_t dsema, dispatch_time_t timeout );

This function will reduce the value of the input semaphore dsema by 1. This function is used. If the value of the dsema semaphore is greater than 0, the thread where the function is located will continue to execute the following statement, and the semaphore value is reduced by 1. If the desema value is 0, this function blocks the current thread from waiting for timeout (note that the timeout type is dispatch_time_t, the number of integer or float types cannot be passed in directly. If the value of desema is added to 1 by the dispatch_semaphore_signal function during the waiting period, and the thread where the function (dispatch_semaphore_wait) is located obtains the semaphore, then proceed to the next step and subtract 1 from the semaphore. If the semaphore is not obtained during the waiting period or the semaphore value is always 0, then when the timeout occurs, the thread in which it is located automatically executes the subsequent statement.

Dispatch_semaphore is a semaphore, but it can also be used as a lock when the total number of signals is set to 1. When there is no waiting condition, its performance is higher than pthread_mutex, but when there is a waiting condition, its performance will decrease a lot. Compared with OSSpinLock, it does not consume CPU resources while waiting.

In the above Code, if the timeout overTime is set to> 2, the synchronization operation can be completed. If overTime

The execution result of the above Code is:

20:47:52. 324 SafeMultiThread [35945: 579032] start of Operation 1 requiring Thread Synchronization
20:47:55. 325 SafeMultiThread [35945: 579032] End of operation 1 requiring Thread Synchronization
20:47:55. 326 SafeMultiThread [35945: 579033] operations that require thread synchronization 2

If you set the timeout time

18:53:24. 049 SafeMultiThread [30834: 434334] start of Operation 1 requiring Thread Synchronization
18:53:25. 554 SafeMultiThread [30834: 434332] operations that require thread synchronization 2
18:53:26. 054 SafeMultiThread [30834: 434334] End of operation 1 requiring Thread Synchronization

 

2.3 NSLock
NSLock * lock = [[NSLock alloc] init]; dispatch_async (dispatch_get_global_queue (queue, 0), ^ {// [lock]; [lock lockBeforeDate: [NSDate date]; NSLog (@ "start 1 of operations requiring thread synchronization"); sleep (2); NSLog (@ "End 1 of operations requiring thread synchronization "); [lock unlock] ;}); dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {sleep (1); if ([lock tryLock]) {// try to obtain the lock, if NO result is returned, the thread NSLog (@ "available lock operations"); [lock unlock];} else {NSLog (@ "lock unavailable operation");} NSDate * date = [[NSDate alloc] initWithTimeIntervalSinceNow: 3]; if ([lock lockBeforeDate: date]) {// try to get the lock in the next 3 S and block the thread. If the thread cannot be obtained within 3 s, NO will be returned and NSLog of the thread will not be blocked (@ "NO timeout, get lock "); [lock unlock];} else {NSLog (@" timeout, no lock obtained ");}});

 

SLock is the most basic lock Object provided by Cocoa, which is also frequently used. In addition to the lock and unlock methods, NSLock also provides the tryLock and lockBeforeDate methods, the previous method attempts to lock the lock. If the lock is unavailable (locked), the thread will not be blocked and NO will be returned. LockBeforeDate: The method attempts to lock before the specified Date. If NO lock is allowed before the specified time, NO is returned.

The execution result of the above Code is:

20:45:08. 864 SafeMultiThread [35911: 575795] start of Operation 1 requiring Thread Synchronization
20:45:09. 869 SafeMultiThread [35911: 575781] Lock unavailable operation
20:45:10. 869 SafeMultiThread [35911: 575795] End of operation 1 requiring Thread Synchronization
20:45:10. 870 SafeMultiThread [35911: 575781] No timeout, get the lock

 

2.4 NSRecursiveLock recursive lock
    //NSLock *lock = [[NSLock alloc] init];    NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        static void (^RecursiveMethod)(int);        RecursiveMethod = ^(int value) {            [lock lock];            if (value > 0) {                NSLog(@"value = %d", value);                sleep(1);                RecursiveMethod(value - 1);            }            [lock unlock];        };        RecursiveMethod(5);    });

 

NSRecursiveLock actually defines a recursive lock, which can be requested multiple times by the same thread without causing a deadlock. This is mainly used in loop or recursive operations.

This code is a typical deadlock. In our thread, RecursiveMethod is called recursively. So each time you enter this block, a lock will be applied. From the second time on, because the lock has been used and has not been unlocked, it needs to wait for the lock to be lifted, this causes a deadlock and the thread is blocked. The debugger outputs the following information:

19:08:06. 393 SafeMultiThread [30928: 449008] value = 5
19:08:07. 399 SafeMultiThread [30928: 449008]-[NSLock lock]: deadlock ('(null )')
19:08:07. 399 SafeMultiThread [30928: 449008]
Break on _ NSLockError () to debug.

In this case, we can use NSRecursiveLock. It allows the same thread to lock Multiple times without causing a deadlock. Recursive locks track the number of times it is locked. Each successful lock must call the unlock operation in balance. Only when this balance is reached can the lock be released for use by other threads.

If we replace NSLock with NSRecursiveLock, the above Code will be correctly executed.

19:09:41. 414 SafeMultiThread [30949: 450684] value = 5
19:09:42. 418 SafeMultiThread [30949: 450684] value = 4
19:09:43. 419 SafeMultiThread [30949: 450684] value = 3
19:09:44. 424 SafeMultiThread [30949: 450684] value = 2
19:09:45. 426 SafeMultiThread [30949: 450684] value = 1

 

2.5 NSConditionLock condition lock
NSMutableArray * products = [NSMutableArray array]; NSInteger HAS_DATA = 1; NSInteger NO_DATA = 0; dispatch_async (unlock, 0), ^ {while (1) {[lock lockWhenCondition: NO_DATA]; [products addObject: [[NSObject alloc] init]; NSLog (@ "produce a product, total: % zi", products. count); [lock unlockWithCondition: HAS_DATA]; sleep (1) ;}}); dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {while (1) {NSLog (@ "wait for product"); [lock lockWhenCondition: HAS_DATA]; [products removeObjectAtIndex: 0]; NSLog (@ "custome a product"); [lock unlockWithCondition: NO_DATA] ;}});

 

When we use multiple threads, sometimes a lock that only locks and unlocks cannot fully satisfy our needs. Because normal locks only care about locks and do not care about which key is used to unlock the lock, while we are dealing with resource sharing, in most cases, the lock can be opened only when certain conditions are met:

The lock is used in the lock in thread 1, so no conditions are required, so the lock is successful, but an integer condition is used in the unlock, it can start other threads waiting for the critical location of the key, and thread 2 needs a key marked as 2, so when thread 1 loops to the last time, in the end, thread 2 is blocked. But even so, NSConditionLock is the same as other locks. It requires the corresponding lock and unlock, but lock, lockWhenCondition: And unlock, unlockWithCondition: can be freely combined, of course this is related to your needs.

The above code execution result is as follows:

20:31:58. 699 SafeMultiThread [31282: 521698] wait for product
20:31:58. 699 SafeMultiThread [31282: 521708] produce a product, total: 1
20:31:58. 700 SafeMultiThread [31282: 521698] custome a product
20:31:58. 700 SafeMultiThread [31282: 521698] wait for product
20:31:59. 705 SafeMultiThread [31282: 521708] produce a product, total: 1
20:31:59. 706 SafeMultiThread [31282: 521698] custome a product
20:31:59. 706 SafeMultiThread [31282: 521698] wait for product
20:32:00. 707 SafeMultiThread [31282: 521708] produce a product, total: 1
20:32:00. 708 SafeMultiThread [31282: 521698] custome a product

2.6. NSCondition
NSCondition * condition = [[NSCondition alloc] init]; NSMutableArray * products = [NSMutableArray array]; dispatch_async (dispatch_get_global_queue (expiration, 0), ^ {while (1) {[condition lock]; if ([products count] = 0) {NSLog (@ "wait for product"); [condition wait];} [products removeObjectAtIndex: 0]; NSLog (@ "custome a product"); [condition unlock] ;}}); dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ {while (1) {[condition lock]; [products addObject: [[NSObject alloc] init]; NSLog (@ "produce a product, total: % zi", products. count); [condition signal]; [condition unlock]; sleep (1 );}});

 

A basic condition lock. Manually control the thread wait and signal.

[Condition lock]; it is generally used to access and modify the same data source in multiple threads at the same time, so that the data source can be accessed and modified only once at the same time. commands of other threads need to wait outside lock, only to unlock for access

[Condition unlock]; used together with lock

[Condition wait]; puts the current thread in the waiting state

[Condition signal]; The CPU sends a signal that the thread does not have to wait, and can continue to execute

The above code execution result is as follows:

20:21:25. 295 SafeMultiThread [31256: 513991] wait for product
20:21:25. 296 SafeMultiThread [31256: 513994] produce a product, total: 1
20:21:25. 296 SafeMultiThread [31256: 513991] custome a product
20:21:25. 297 SafeMultiThread [31256: 513991] wait for product
20:21:26. 302 SafeMultiThread [31256: 513994] produce a product, total: 1
20:21:26. 302 SafeMultiThread [31256: 513991] custome a product
20:21:26. 302 SafeMultiThread [31256: 513991] wait for product
20:21:27. 307 SafeMultiThread [31256: 513994] produce a product, total: 1
20:21:27. 308 SafeMultiThread [31256: 513991] custome a product

 

2.7, pthread_mutex
_ Block pthread_mutex_t theLock; pthread_mutex_init (& theLock, NULL); dispatch_async (dispatch_get_global_queue (queue, 0), ^ {pthread_mutex_lock (& theLock ); NSLog (@ "start 1 of operations requiring thread synchronization"); sleep (3); NSLog (@ "End 1 of operations requiring thread synchronization "); pthread_mutex_unlock (& theLock) ;}); dispatch_async (dispatch_get_global_queue (queue, 0), ^ {sleep (1); pthread_mutex_lock (& theLock ); NSLog (@ "operation requiring thread synchronization 2"); pthread_mutex_unlock (& theLock );});

 

The multi-thread locking method is defined in C language. 1: pthread_mutex_init (pthread_mutex_t mutex, const pthread_mutexattr_t attr); initialize the lock variable mutex. Attr is the lock attribute, and NULL is the default attribute. 2: pthread_mutex_lock (pthread_mutex_t mutex); lock 3: pthread_mutex_tylock (* pthread_mutex_t * mutex); lock, but unlike 2, when the lock is in use, the returned value is EBUSY, instead of pending. 4: pthread_mutex_unlock (pthread_mutex_t * mutex); release lock 5: pthread_mutex_destroy (pthread_mutex_t * mutex); release the code after use. The execution result is as follows: 21:13:32. 440 SafeMultiThread [31429: 548869] operations requiring thread synchronization 1 start 21:13:35. 445 SafeMultiThread [31429: 548869] the operation for thread synchronization 1 ends 21:13:35. 446 SafeMultiThread [31429: 548866] operations that require thread synchronization 2

 

2.8, pthread_mutex (recursive)
    __block pthread_mutex_t theLock;    //pthread_mutex_init(&theLock, NULL);    pthread_mutexattr_t attr;    pthread_mutexattr_init(&attr);    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);    pthread_mutex_init(&lock, &attr);    pthread_mutexattr_destroy(&attr);    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        static void (^RecursiveMethod)(int);        RecursiveMethod = ^(int value) {            pthread_mutex_lock(&theLock);            if (value > 0) {                NSLog(@"value = %d", value);                sleep(1);                RecursiveMethod(value - 1);            }            pthread_mutex_unlock(&theLock);        };        RecursiveMethod(5);    });

 

This is a recursive lock of pthread_mutex to prevent deadlocks in recursive situations. Similar to NSRecursiveLock recursive locks.

If you use pthread_mutex_init (& theLock, NULL); to initialize the lock, the above Code will experience a deadlock. If recursive locks are used, there is no problem.

 

2.9. OSSpinLock
_ Block OSSpinLock theLock = OS _SPINLOCK_INIT; dispatch_async (dispatch_get_global_queue (queue, 0), ^ {OSSpinLockLock (& theLock); NSLog (@ "start 1 of operations requiring thread synchronization "); sleep (3); NSLog (@ "end of Operation 1 for thread synchronization"); OSSpinLockUnlock (& theLock) ;}); dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^ {OSSpinLockLock (& theLock); sleep (1); NSLog (@ "operation requiring thread synchronization 2"); OSSpinLockUnlock (& theLock );});

The OSSpinLock has the highest performance. The principle is very simple, that is, do while busy all the time. Its disadvantage is that it consumes a lot of CPU resources when waiting, so it is not suitable for long-time tasks. OSSpinLock is no longer secure. Please use it with caution.

Iii. Performance Comparison

The following figure shows how long it takes to unlock the 1000000 lock:

OSSpinLock: 46.15 MS
Dispatch_semaphore: 56.50 MS
MS pthread_mutex: 178.28
NSCondition: 193.38 MS
NSLock: 175.02 MS
Pthread_mutex (recursive): 172.56 MS
NSRecursiveLock: 157.44 MS
NSConditionLock: 490.04 MS
@ Synchronized: 371.17 MS

In general:

OSSpinLock and dispatch_semaphore are much more efficient than others.

@ Synchronized and NSConditionLock are inefficient.

We recommend that you use dispatch_semaphore when considering the performance of OSSpinLock during development.

If the performance is not considered, it is only convenient to figure it out, use @ synchronized.

 

 

Author: jingming Baba

The copyright of this article is shared by the author and the blog. You are welcome to repost it, but you must keep this statement and provide a connection to the original article on the article page. Clear Saup.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.