IOS Multithreaded Demo

IOS multithreading –NSThread

IOS multithreading -GCD details

IOS multithreading –NSOperation

IOS Multithreading — Thread Safety (Thread locking)

IOS multi-threaded interview questions



First, it should be noted that all of the methods called in the following interview questions (for example, the first interview question called interview1) are called from the main thread.

Interview Question 1

- (void)interview1{
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    dispatch_async(queue, ^{
        NSLog(@"1 - % @",[NSThread currentThread]);
        [self performSelector:@selector(test1) withObject:nil afterDelay:.0f];
        NSLog(@"3 - % @",[NSThread currentThread]);
    });
}

- (void)test1{
    NSLog(@"2 - % @",[NSThread currentThread]); } / / * * * * * * * * * * * * * * * printing result * * * * * * * * * * * * * * * 2019-12-30 17:37:58. 427558 + 0800 MultithreadingDemo [39113-4277962] 1 -- -- -- < NSThread:  0x600001922d40>{number = 6, Name = (null)} 2019-12-30 17:37:58.427659+0800 MultithreadingDemo[39113:4277962] 3-- <NSThread: 0x600001922d40>{number = 6, name = (null)}Copy the code

Explanation: the performSelector: withObject: afterDelay: is the nature of the Runloop add a timer (even if the delay time is 0 seconds). The test1 method is not executed because the dispatch_async asynchronous function starts a new child thread to execute the task, and the child thread does not start Runloop by default.

We can manually start runloop to ensure test1 is called by adding a line of code to the block [[NSRunLoop currentRunLoop] run]; .


If we change the asynchronous function to synchronous function, let’s look at the result:

- (void)interview1{
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    dispatch_sync(queue, ^{
        NSLog(@"1 - % @",[NSThread currentThread]);
        [self performSelector:@selector(test1) withObject:nil afterDelay:.0f];
        NSLog(@"3 - % @",[NSThread currentThread]);
    });
}

- (void)test1{
    NSLog(@"2 - % @",[NSThread currentThread]); } / / * * * * * * * * * * * * * * * printing result * * * * * * * * * * * * * * * 2019-12-30 17:47:01. 936609 + 0800 MultithreadingDemo [39150-4282068] 1 -- -- -- < NSThread:  0x6000009660c0>{number = 1, Name = main} 2019-12-30 17:47:01.936724+0800 MultithreadingDemo[39150:4282068] 3-- <NSThread: 0x6000009660c0>{number = 1, Name = main} 2019-12-30 17:47:01.936904+0800 MultithreadingDemo[39150:4282068] 2-- <NSThread: 0x6000009660c0>{number = 1, name = main}Copy the code

The synchronization function adds tasks that are executed in the current thread, which is the main thread. The main thread’s Runloop is started, so test1 calls it. Although the delay is 0 seconds, the timer added to the Runloop does not fire immediately. Instead, the Runloop must wake up first, which takes some time, so it prints 3 and then 2.


We’ll put the performSelector: withObject: afterDelay: replace performSelector: withObject: look at the results:

- (void)interview1{
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    dispatch_async(queue, ^{
        NSLog(@"1 - % @",[NSThread currentThread]);
        [self performSelector:@selector(test1) withObject:nil];
        NSLog(@"3 - % @",[NSThread currentThread]);
    });
}

- (void)test1{
    NSLog(@"2 - % @",[NSThread currentThread]); } / / * * * * * * * * * * * * * * * printing result * * * * * * * * * * * * * * * 2019-12-30 17:54:18. 072035 + 0800 MultithreadingDemo [39183-4285659] 1 -- -- -- < NSThread:  0x60000303c300>{number = 3, Name = (null)} 2019-12-30 17:54:18.072136+0800 MultithreadingDemo[39183:4285659] 2-- <NSThread: 0x60000303c300>{number = 3, Name = (null)} 2019-12-30 17:54:18.072215+0800 MultithreadingDemo[39183:4285659] 3-- <NSThread: 0x60000303c300>{number = 3, name = (null)}Copy the code

Explanation: the performSelector: withObject: function is has nothing to do with timer, so won’t be added to the Runloop, so it is in the order 1, 2, 3.

Note: Any method in the performSelector family whose name contains afterDelay or waitUntilDone is timer specific should be aware of these problems.

Interview question 2

- (void)interview2{
    NSThread *thread = [[NSThread alloc] initWithBlock:^{
       NSLog(@"1 - % @",[NSThread currentThread]);
    }];
    [thread start];
    
    [self performSelector:@selector(test2) onThread:thread withObject:nil waitUntilDone:YES];
}

- (void)test2{
    NSLog(@"2 - % @",[NSThread currentThread]); } / / * * * * * * * * * * * * * * * run results (flash) back * * * * * * * * * * * * * * * 2019-12-31 08:36:07. 132133 + 0800 MultithreadingDemo (40268-4493885) 1---<NSThread: 0x6000010d9880>{number = 6, Name = (null)} 2019-12-31 08:36:07.432190+0800 MultithreadingDemo[40268:4493455] *** Terminating app due to uncaught exception'NSDestinationInvalidException', reason: '*** -[Interview performSelector:onThread:withObject:waitUntilDone:modes:]: target thread exited while waiting for the perform'
Copy the code

Description: The cause of the flash is target thread exited. The test2 method is executed on a thread, but the thread is executed after the NSLog(@”1– %@”,[NSThread currentThread]); This code ends, so by the time test2 is executed, the thread will no longer exist.

If we want our code to work, we can use Runloop knowledge to keep threads alive. Start the runloop by adding a source to the current runloop (or exiting if the runloop does not have a source, NSTime, or Obserer). Add two lines of code to the block of thread, as shown below:

NSThread *thread = [[NSThread alloc] initWithBlock:^{
       NSLog(@"1 - % @",[NSThread currentThread]); // Add one to the current runloopsource(If one of the runloopssource[[NSRunLoop currentRunLoop] addPort:[NSPort new]forMode:NSRunLoopCommonModes]; / / and then start the runloop [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: [NSDate distantFuture]]. }];Copy the code

3. Interview question 3

- (void)interview3{
    NSLog(@"Execute task 1--%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_sync(queue, ^{
        NSLog(@"Task 2--%@",[NSThread currentThread]);
    });
    
    NSLog(@"Mission 3--%@",[NSThread currentThread]);
}
Copy the code

Running results:

Got stuck right after mission one.

Explanation:

Interview3 method is executed in the main thread, after the execution of task 1, add task 2 to the main queue (serial queue) through the synchronization function, because the synchronization added task must be executed immediately, but the current task (Interview3) in the serial queue is not finished, can not arrange task 2 to execute. Therefore, task 2 cannot be executed until the current task (Interview3) is finished, and Interview3 cannot continue to execute until task 2 is finished, resulting in mutual waiting deadlock.

4. Interview question 4

- (void)interview4{
    NSLog(@"Execute task 1--%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        NSLog(@"Task 2--%@",[NSThread currentThread]);
    });
    
    NSLog(@"Mission 3--%@",[NSThread currentThread]); } / / * * * * * * * * * * * * * * * printing result * * * * * * * * * * * * * * * 2019-12-31 10:06:37. 782135 + 0800 MultithreadingDemo (41281-4538099) Perform task 1--<NSThread: 0x600003cd1d80>{number = 1, name = main} 2019-12-31 10:06:37.782244+0800 MultithreadingDemo[41281:4538099] 0x600003cd1d80>{number = 1, name = main} 2019-12-31 10:06:37.782574+0800 MultithreadingDemo[41281:4538099] 0x600003cd1d80>{number = 1, name = main}
Copy the code

Explanation:

This is just an asynchronous function instead of a synchronous function compared to the previous interview question. After the execution of task 1, add task 2 through the asynchronous function. Although the asynchronous function has the ability to start the child thread, it does not start the child thread because it is in the main queue and the tasks in the main queue are executed in the main thread. Since task 2 was added by the asynchronous function, it is not necessary to wait for task 2 to continue, and the serial queue will arrange task 2 to be executed after the current task (Interview4) completes. So there is no deadlock.

5. Interview question 5

- (void)interview5{
    NSLog(@"Execute task 1--%@",[NSThread currentThread]);
    
    dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        NSLog(@"Task 2--%@",[NSThread currentThread]);
        
        dispatch_sync(queue, ^{
            NSLog(@"Mission 3--%@",[NSThread currentThread]);
        });
    
        NSLog(@"Mission 4--%@",[NSThread currentThread]);
    });
    
    NSLog(@"Mission 5--%@",[NSThread currentThread]); / / * * * * * * * * * * * * * * * to print the results (print card dead after 1, 5, 2) * * * * * * * * * * * * * * * 2019-12-31 10:45:29. 071774 + 0800 MultithreadingDemo (41379-4551961) Perform task 1--<NSThread: 0x6000038460c0>{number = 1, Name = main} 2019-12-31 10:45:29.071923+0800 MultithreadingDemo[41379:4551961] 0x6000038460c0>{number = 1, Name = main} 2019-12-31 10:45:29.071932+0800 MultithreadingDemo[41379:4552048] 0x600003824f40>{number = 6, name = (null)} }Copy the code

Explanation: Print task 1 first, and then create a serial queue, and through the asynchronous function is added to the queue a task (block1), an asynchronous function opens a child thread and block1 in child thread to execute, open the child thread is time-consuming, but also can continue to perform asynchronous tasks do not need to wait for it at the back of the code, So print task 5 is executed before block1.

Then look at block1 task block. First print task 2, and then add block2 task block through synchronization function, which needs to be executed immediately. However, the queue where Block1 is located is a serial queue, and block1 task block has not been executed, so it needs to wait for block1 execution first. Block1 waits for block2 to complete before it can proceed, resulting in a deadlock between waits.

6. Interview question 6

- (void)interview6{

    NSLog(@"Execute task 1--%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue2 = dispatch_queue_create("myqueu2", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue, ^{
        NSLog(@"Task 2--%@",[NSThread currentThread]);
        
        dispatch_sync(queue2, ^{
            NSLog(@"Mission 3--%@",[NSThread currentThread]);
        });
        
        NSLog(@"Mission 4--%@",[NSThread currentThread]);
    });
    
    NSLog(@"Mission 5--%@",[NSThread currentThread]); } / / * * * * * * * * * * * * * * * printing result * * * * * * * * * * * * * * * 2019-12-31 11:01:47. 812260 + 0800 MultithreadingDemo (41405-4557566) Perform task 1--<NSThread: 0x600002836180>{number = 1, Name = main} 2019-12-31 11:01:47.812470+0800 MultithreadingDemo[41405:4557566] 0x600002836180>{number = 1, Name = main} 2019-12-31 11:01:47.812488+0800 MultithreadingDemo[41405:4557684] 0x600002830980>{number = 5, Name = (null)} 2019-12-31 11:01:47.812567+0800 MultithreadingDemo[41405:4557684] 0x600002830980>{number = 5, Name = (null)} 2019-12-31 11:01:47.812648+0800 MultithreadingDemo[41405:4557684] 0x600002830980>{number = 5, name = (null)}Copy the code

Explanation:

This is a new queue (serial queue or concurrent queue), and the block1 and block2 task blocks are placed in different queues.

Printing task 1 and then task 5 is the same as before. The asynchronous function then starts the child thread to execute block1, which prints task 2, and then adds block2 to another queue via the synchronous function. Since the two blocks belong to different queues, block2 can be executed immediately without deadlock, so it is followed by print task 3. Finally print task 4.

7. Interview question 7

- (void)interview7{
    NSLog(@"Execute task 1--%@",[NSThread currentThread]);
    
    dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"Task 2--%@",[NSThread currentThread]);
        
        dispatch_sync(queue, ^{
            NSLog(@"Mission 3--%@",[NSThread currentThread]);
        });
        
        NSLog(@"Mission 4--%@",[NSThread currentThread]);
    });
    
    NSLog(@"Mission 5--%@",[NSThread currentThread]); } / / * * * * * * * * * * * * * * * printing result * * * * * * * * * * * * * * * 2019-12-31 11:14:27. 690008 + 0800 MultithreadingDemo (41445-4562142) Perform task 1--<NSThread: 0x6000011badc0>{number = 1, Name = main} 2019-12-31 11:14:27.690102+0800 MultithreadingDemo[41445.4562142] 0x6000011badc0>{number = 1, Name = main} 2019-12-31 11:14:27.690122+0800 MultithreadingDemo[41445.4562301] 0x6000011f5900>{number = 3, Name = (null)} 2019-12-31 11:14:27.690202+0800 MultithreadingDemo[41445.4562301] 0x6000011f5900>{number = 3, Name = (null)} 2019-12-31 11:14:27.690285+0800 MultithreadingDemo[41445.4562301] 0x6000011f5900>{number = 3, name = (null)}Copy the code

Explanation:

This replaces a serial queue with a concurrent queue compared to question 5.

Printing task 1 and then task 5 is the same as before. Then the asynchronous function will start the child thread to execute the block1 task block. In block1, task 2 is printed first, and then the synchronous function is used to add block2 task block to the concurrent queue. The concurrent queue can arrange the next task execution without waiting for the completion of the previous task, so block2 can execute the printing task 3 immediately. Finally, print task 4.