This is the 21st day of my participation in the August Text Challenge.More challenges in August
Write in front: iOS underlying principle exploration is my usual development and learning in the accumulation of a section of advanced road. Record my continuous exploration of the journey, I hope to be helpful to all readers.Copy the code
The directory is as follows:
- IOS underlying principles of alloc exploration
- The underlying principles of iOS are explored
- The underlying principles of iOS explore the nature of objects & isa’s underlying implementation
- Isa-basic Principles of iOS (Part 1)
- Isa-basic Principles of iOS (Middle)
- Isa-class Basic Principles of iOS Exploration (2)
- IOS fundamentals explore the nature of Runtime Runtime & methods
- Objc_msgSend: Exploring the underlying principles of iOS
- Slow lookups in iOS Runtime
- A dynamic approach to iOS fundamentals
- The underlying principles of iOS explore the message forwarding process
- Dyld (part 1)
- IOS Basic Principles of application loading principle dyld (ii)
- IOS basic principles explore the loading of classes
- The underlying principles of iOS explore the loading of categories
- IOS underlying principles to explore the associated object
- IOS underlying principle of the wizard KVC exploration
- Exploring the underlying principles of iOS: KVO Principles | More challenges in August
- Exploring the underlying principles of iOS: Rewritten KVO | More challenges in August
- The underlying principles of iOS: Multi-threading | More challenges in August
- GCD functions and queues in iOS
- GCD principles of iOS (Part 1)
- IOS Low-level – What do you know about deadlocks?
- IOS Low-level – Singleton destruction is possible?
- IOS Low-level – Dispatch Source
- IOS bottom – a fence letter blocks the number
- IOS low-level – Be there or be Square semaphore
- IOS underlying GCD – In and out into a scheduling group
- Basic principles of iOS – Basic use of locks
- IOS underlying – @synchronized Flow analysis
- IOS low-level – The principle of lock exploration
Summary of the above column
- Summary of iOS underlying principles of exploration
Sort out the details
- Summary of iOS development details
preface
In the previous content, we explained the basic use of various locks through the basic use of locks; Then, it explores the underlying structure and process of @synchronized which is very convenient to use in the development. Finally, we explore the process of NSLock, NSRecursiveLock, NSCondition and NSConditionLock. Through these three articles, we have gained a deeper understanding of the principles and use of locks in iOS development. Today, we’ll implement a read-write lock ourselves that we didn’t cover in the previous article. All right, let’s get started.
Read/write Lock requirements
To implement a read-write lock, let’s think about, if we were to use a read-write lock, what requirements would you want the lock to fulfill?
- First of all, it needs to meet the requirements of multi-threading, multi-reading and single-writing;
- Second, to meet read and write mutually exclusive, write mutually exclusive;
- Finally, read and write operations must not block on the current thread;
Read/write lock implementation
Based on the above three requirements, we need to summarize the knowledge points summarized before and select the type based on the knowledge of GCD and lock to see which points can meet our requirements and have the opportunity to be used as specific implementation content.
Technology selection
The maximum number of concurrent requests can be controlled by dispatch_semaphore_SIGNAL and dispatch_semaphore_WAIT. However, when using multithreading, even if we control the maximum number of concurrent requests to 1, we can not determine which one is used first, so, for waiting and sending signals, there is no way to start, so, semaphore is not good.
The most direct function of the fence functions (dispatch_barrier_async, dispatch_barrier_sync) is to control the task execution order and synchronization. Only the same concurrent queue can be controlled. Also selected.
Dispatch group (dispatch_group_t) The dispatch_group_t dispatch group controls the execution sequence of tasks. The tasks can be executed concurrently as long as the enter and leave match properly. Therefore, it does not meet the requirements. In addition, Dispatch Source is not often used in practice, but custom Timer can be realized. Pass does not meet the requirements.
Ok, with two ideas, let’s try to implement read/write locks.
The Demo implementation
First, define an object to which we are being read or written:
@interface LockTest : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *like;
@end
Copy the code
Next, we’ll customize a concurrent queue:
@property (nonatomic, strong) dispatch_queue_t safeQueue; . _cusQueue = dispatch_queue_create("rw.test.queue", DISPATCH_QUEUE_CONCURRENT);
Copy the code
Here is the focus of this time, read and write content implementation, read we via dispatch_barrier_async to implement:
- (NSObject *)safeReadPro:( NSString *)pro
andObject:( NSObject *)obj {
__block NSObject *a;
dispatch_barrier_async(_safeQueue, ^{
NSLog(@"Start reading --");
sleep( ( arc4random() % 2)); a = [obj valueForKey:pro]; NSLog(@"Read - % @ % @", a , [NSThread currentThread]);
});
NSLog(@"% @", a);
return a;
}
Copy the code
Write (dispatch_barrier_async); write (dispatch_barrier_async);
- (void)safeSetPro:( NSString *)pro
andNewPro:( NSObject *)nPro
andObject:( NSObject *)obj {
dispatch_barrier_async(_safeQueue, ^{
NSLog(@"Start writing --");
sleep( ( arc4random() % 2)); [obj setValue:nPro forKey:pro]; NSLog(@"Write - % @ % @", [obj valueForKey:pro], [NSThread currentThread]);
});
}
Copy the code
Please note, very detailed oh, we have simulated delay operation.
Next, start testing:
Whoops, it prints a bunch of null;
(null)
(null)
(null)
(null)
(null)
(null)
(null)
(null)
(null)
(null)
(null)
(null)
(null)
(null)
(null)
(null)
(null)
(null)
(null)
(null)
Copy the code
Why is that? Dispatch_barrier_async = dispatch_barrier_async = dispatch_barrier_async So, what’s the solution?
Instead of dispatch_barrier_sync, it’s easy, and if we didn’t do dispatch_barrier_sync, then we wouldn’t be able to achieve mutual exclusion between reads and writes. We’re constantly assigning values, however, we’re not sure which values we’re reading, add dispatch_barrier_sync, we can’t write when we’re reading, we can’t read when we’re writing, so it’s a perfect read/write mutex. Then run the project and look at the log:
Start writing -- write --superman-0--<NSThread: 0x600003479840>{number = 7, name = (null)} --<NSThread: 0x600003479840> 0x60000347a740>{number = 6, name = (null)} (null) 0x600003418140>{number = 8, name = (null)} Superman -0 starts to write -- write -- Superman -nick-0--<NSThread: 0x60000347A740 >{number = 6, name = (null)} Start write -- write --superman -- nick-1--<NSThread: 0x60000347a740>{number = 6, name = (null)} 0x600003429380>{number = 5, name = (null)} Superman -nick-1 starts reading --superman -- 0--<NSThread: 0x600003432dc0>{number = 3, name = (null)} Superman -0 starts to write -- write -- Superman -1--<NSThread: 0x60000347a740>{number = 6, name = (null)} Start write -- write --superman -- nick-2--<NSThread: 0x60000347a740>{number = 6, name = (null)} 0x60000347AD80 >{number = 9, name = (null)} Superman -1 starts writing -- write -- Superman -2--<NSThread: 0x60000347a740>{number = 6, name = (null)} 0x600003429A00 >{number = 4, name = (null)} Superman -nick-2 starts to write -- write --superman-3--<NSThread: 0x60000347AD80 >{number = 9, name = (null)} Start write -- write --superman -- nick-3--<NSThread: 0x60000347AD80 >{number = 9, name = (null)} 0x600003434800>{number = 10, name = (null)} Superman -nick-3 starts reading -- read -- Superman -3--<NSThread: 0x60000347ADC0 >{number = 11, name = (null)} Superman -3 starts writing -- write -- Superman -4--<NSThread: 0x600003434800>{number = 10, name = (null)} Start write -- write --superman -- nick-4--<NSThread: 0x600003434800>{number = 10, name = (null)} 0x60000347A640 >{number = 12, name = (null)} Superman -4 starts reading -- read -- Superman -nick-4--<NSThread: 0x600003436FC0 >{number = 13, name = (null)} Superman -nick-4 starts writing -- Write -- Superman -5--<NSThread: 0x60000347A640 >{number = 12, name = (null)} 0x60000347A740 >{number = 14, name = (null)} Superman -5 starts to write -- write -- Superman -nick-5--<NSThread: 0x60000347A640 >{number = 12, name = (null)} 0x60000347A640 >{number = 12, name = (null)} 0x600003432CC0 >{number = 15, name = (null)} Superman -nick-5 starts to write -- Write -- Superman -nick-6--<NSThread: 0x60000347A640 >{number = 12, name = (null)} 0x6000034705C0 >{number = 16, name = (null)} Superman -6 starts reading -- read -- Superman -nick-6--<NSThread: 0x600003418580>{number = 17, name = (null)} Superman -nick-6 starts to write -- write --superman-7--<NSThread: 0x6000034705C0 >{number = 16, name = (null)} 0x60000343C500 >{number = 18, name = (null)} Superman -7 starts to write -- write -- Superman -nick-7--<NSThread: 0x6000034705C0 >{number = 16, name = (null)} 0x60000347AA40 >{Number = 19, name = (null)} Superman - Nick -7 Starts writing --Copy the code
Perfect. A read-write lock, and we’re done.