Thread safety

  • Resource sharing: a resource that can be accessed by multiple objects; Such as global objects, variables, files
    • In a multi-threaded environment, a shared resource may be shared by multiple threads, which means that multiple threads may operate on the same resource
    • When multiple threads operate on the same resource, it is easy to cause data corruption and data security problems. Data may be lost, added, or corrupted
  • Thread-safe: The same resource can be read and written by multiple threads at the same time, but still get the correct result. This is called thread-safe

Buy a ticket case

@implementation ViewController{
    NSInteger _ticket;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    _ticket = 10; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSThread *th1 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
    th1.name = @"th1";
    [th1 start];
    
    NSThread *th2 = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil];
    th2.name = @"th2"; [th2 start]; } - (void)saleTicket{
    while (1) {
        if (_ticket>0) {
            _ticket--;
            NSLog(@"%@ remaining tickets % ZD tickets"[NSThread currentThread].name,_ticket);
        }else{   
            NSLog(@"%@ tickets sold out"[NSThread currentThread].name);
            break; }}}Copy the code
  • Result of resource preemption: Data is incorrect
  • Cause: THE CPU dispatches to the second thread in the middle of the first thread before printing. After the second thread prints, it dispatches back to the first thread. At this time, it continues to print according to the number of code execution lines and variable data saved by the first thread

  • Solution: Add mutex (synchronous lock)
    • Mutex is a thread synchronization technique
    • Mutex should be limited in scope, but must lock the read and write parts of the resource
    • Locked programs execute less efficiently than unlocked programs because threads wait to be unlocked
    • At this point, the thread goes to sleep and the CPU does not schedule the thread
    • Sacrificing performance for security
Synchronized means that a thread must complete the lock before any other thread can enter. @synchronized means that a thread must complete the lock before any other thread can enter Self is a lock object, and anything that inherits from NSObject can be called a lock object except that the lock object can be locked only if it's global, not locally. Won't lock not because obj is released, and semaphore obj must be written for the global variable) outside thread execution method because the self is the most convenient time for you to get the global object Lock this code, generally do not write on the client side, only to write on the server side, because the data is saved on the server, generally only the server has the problem of data sharing And there's another reason: locking is a resource hog, so clients don't use it either, because clients are more fluid- (void)saleTicket{
    while (1) {
        @synchronized (self) {
            if (_ticket>0) {
                _ticket--;
                NSLog(@"%@ remaining tickets % ZD tickets"[NSThread currentThread].name,_ticket);
            }else{  
                NSLog(@"%@ tickets sold out"[NSThread currentThread].name);
                break; }}}}Copy the code

Mutex and spin locks

  • In common
    • Both ensure that only one thread executes the lockscope code at a time
  • The difference between
    • Mutex: If it is found that another thread is executing the locked code, the thread will go to sleep and wait for the other thread to complete the execution. After the lock is unlocked, the thread will return to the ready state and wait for the CPU to rescheduled
    • Spin lock: If another thread is detected executing the locked code, the thread will wait in an infinite loop for the locked code to complete

The atomic properties

  • Nonatomic: Non-atomic attribute
  • -Leonard: It’s an atomic property
    • Thread-safe, property modifiers designed for multithreading are the default
    • Features: Single write and read
    • Single write Multiple read: Only one thread can execute the setter method at a time, but multiple threads can execute the getter method
    • The setter for the atomic property has a lock inside it called a spin lock
    • Setter methods for atomic properties are thread-safe; However, getter methods are not thread-safe

Simulated atomic property

@interface ViewController(a)

@property(atomic,assign)int age;

@end

@implementation ViewController
// When setter and getter methods are overridden, underlined member variables are not automatically generated
@synthesizeage = _age; - (void)setAge:(int)age{
    // Mutex simulates spin locks
    @synchronized (self) {
        _age = age;
    }
}

-(int)age{
    return _age;
}
Copy the code

expand

  • Apple declares all properties as nonatomic, and atomic properties perform almost the same as non-atomic properties
  • Try to hand over the business logic of locking and resource snatching to the server to reduce the pressure on mobile clients
  • For a smooth user experience, UIKit library threads are not safe, so we need to update the UI on the main thread (UI thread)
  • All classes that contain NSMutable are thread-unsafe; When doing multithreading development, we need to pay attention to the thread safety problem of multithreading simultaneously operating mutable objects

Interthread communication (asynchronously loading network images)

@interface ViewController(a)

@property (weak.nonatomic) IBOutlet UIImageView *imgView;

@end- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    // The subthread loads network data, and the main thread updates the UI or other NSMutable data
    [self performSelectorInBackground:@selector(loadImg) withObject:nil]; } - (void)loadImg{
    //url: uniform resource locator
    // Set to network path image
    NSURL *url = [NSURL URLWithString:@"http://www.gaibar.com/uploads/allimg/160218/40528-16021R35336-lp.jpg"];
    // Go to the network according to the path to read image data
    NSData *data = [NSData dataWithContentsOfURL:url];
    
    NSLog(@" before reading %@"[NSThread currentThread]);
    // Main thread execution
    // Parameter 3: indicates whether to wait for the main thread method to finish executing before executing the code following this line
    [self performSelectorOnMainThread:@selector(updateUI:) withObject:data waitUntilDone:NO];
    
    NSLog(@" after reading %@"[NSThreadcurrentThread]); } - (void)updateUI:(NSData *)data{
    NSLog(Update @ "% @"[NSThread currentThread]);
    _imgView.image = [UIImage imageWithData:data];
}
Copy the code