Summary and Analysis of iOS Multithreaded Interview Questions

Preface

In fact, before I wrote this blog, I was also looking up bad materials, but I found that many people on the Internet said that the summary of content was not correct, resulting in a lot of pits for myself, so I want to recapitulate it, make a reference for myself, and also review it. Of course, I may have something wrong, I hope it can be corrected.

Combing Knowledge Points

A brief description of basic concepts

  • 1. Synchronization function dispatch_sync must wait for the current statement to complete before proceeding to the next block task

  • 2. Asynchronous function dispatch_async can execute the next statement without waiting for the current statement to be executed, which opens the thread execution block, a pronoun for asynchronous multithreading, with the exception of the main queue

It's important to restore the most basic writing

- (void)syncTest{
    // Add Task to Queue-->Function
    // Task_ t ref c object
    dispatch_block_t block = ^{
        NSLog(@"hello GCD");
    };
    //Serial Queue
    dispatch_queue_t queue = dispatch_queue_create("com.ycx.cn", NULL);
    // function
    dispatch_async(queue, block);
}

The above is to split up the synchronization function steps for ease of understanding

Next, I will test it with actual examples and summarize it with reasonable theory knowledge. In order to make it easy for you to read it, I copied it all in code form. No screenshots, it will also make it easier for you to find the type of topic you want to see faster. Of course, I also hope that the big guys will give comments and new topic types to let me add. 🙏🙏🙏🙏

I recommend you to analyze before you see the results, I verified when a lot of rollover 😂😂😂😂

Example 1:

/**
 Main Queue Synchronization
 No threading
 */
- (void)mainSyncTest{
    
    NSLog(@"0");
    // etc.
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"1");
    });
    NSLog(@"2");
}

Resolution: Main Queue + Synchronization Function (sync function) produces Deadlock 12 waiting for each other

Example 2:

/**
 Serial synchronization queue: FIFO: FIFO: FIFO
 */
- (void)serialSyncTest{
    //1:Create a serial queue
    dispatch_queue_t queue = dispatch_queue_create("ycx", DISPATCH_QUEUE_SERIAL);
    for (int i = 0; i<20; i++) {
        dispatch_sync(queue, ^{
            NSLog(@"%d-%@",i,[NSThread currentThread]);
        });
    }
}

Parsing: Serial Queue FIFO Although 20 tasks have been added in a row, they must be executed sequentially, so the result is to print 1-19 sequentially

Example 3:

/**
 Main Queue Asynchronous
 No threading sequence
 */
- (void)mainAsyncTest{
    NSLog(@"0");
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"1---%@",[NSThread currentThread]);
    });
    NSLog(@"2---%@",[NSThread currentThread]);
}

Parse: primary queue serial + asynchronous or sequential but 1 must wait for 2 to execute

Example 4:

/**
 Global Asynchronization
 Global Queue: A Concurrent Queue
*/
- (void)globalAsyncTest{
    
    for (int i = 0; i<20; i++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"%d-%@",i,[NSThread currentThread]);
        });
    }
    NSLog(@"hello queue");
}

Parsing: global queue concurrent + asynchronous multi-threaded synchronous execution or 20 executions but different threads execution times are random

Example 5:

/**
 Asynchronous Concurrency: Threads are not necessarily opened with asynchronous functions
 */
- (void)concurrentAsyncTest{
    //1:Create a concurrent queue
    dispatch_queue_t queue = dispatch_queue_create("ycx", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i<20; i++) {
        dispatch_async(queue, ^{
            NSLog(@"%d-%@",i,[NSThread currentThread]);
        });
    }
    NSLog(@"hello queue");
}

Resolution: As a result, almost another thread will be opened, but several threads will be opened indefinitely to see how the thread pool is dispatched

Example 6:

/**
 Global Synchronization
 Global Queue: A Concurrent Queue
 */
- (void)globalSyncTest{
    for (int i = 0; i<20; i++) {
        dispatch_sync(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"%d-%@",i,[NSThread currentThread]);
        });
    }
    NSLog(@"hello queue");
}

Resolution: Global Queue Concurrency + Synchronization executes NSLog last execution in turn, no new threads are opened

Example 7:

/**
 Synchronization Concurrency: Blocks the synchronization lock queue: resume supend thread operation, whether queue pending tasks can be executed
 */
- (void)concurrentSyncTest{

    //1:Create a concurrent queue
    dispatch_queue_t queue = dispatch_queue_create("ycx", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i<20; i++) {
        dispatch_sync(queue, ^{
            NSLog(@"%d-%@",i,[NSThread currentThread]);
        });
    }
    NSLog(@"hello queue");
}

Resolution: The global queue results above are the same

Example 8:

- (void)textDemo1{
    dispatch_queue_t queue = dispatch_queue_create("ycx", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"1");
    dispatch_async(queue, ^{
        NSLog(@"2");
        dispatch_sync(queue, ^{
            NSLog(@"3");
        });
        NSLog(@"4");
    });
    NSLog(@"5");
}

Resolution: Concurrent Queue Asynchronous + Synchronous Result: 1 5 and (234) Concurrency That is, 234 order does not change and 5 may occur anywhere between them with a probability of 15234

Example 9:

- (void)textDemo{
    
    dispatch_queue_t queue = dispatch_queue_create("ycx", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"1");
    // time consuming
    dispatch_async(queue, ^{
        NSLog(@"2");
        dispatch_async(queue, ^{
            NSLog(@"3");
        });
        NSLog(@"4");
    });
    NSLog(@"5");
}

Parsing: Concurrent Queue Asynchronous + Asynchronous Result: 1 The first 2 precedes 34, 34 positions are indefinite and 5 can be anywhere in 234. Probably 15243. In general, the main thread will certainly execute more stably and the other thread will execute tasks slightly slower. Although concurrent, the process of fast CPU switching is intrinsic, so the approximate rate is 15243

Example 10:

/**
 Serial Asynchronous Queue
 */
- (void)serialAsyncTest{
    //1:Create a serial queue
    dispatch_queue_t queue = dispatch_queue_create("ycx", DISPATCH_QUEUE_SERIAL);
    for (int i = 0; i<20; i++) {
        dispatch_async(queue, ^{
            NSLog(@"%d-%@",i,[NSThread currentThread]);
        });
    }
    NSLog(@"hello queue");
}

Parse: Actually, like the first example, the main queue is also a serial queue, so the serial queue is asynchronous, does not open threads, and asynchronous functions are executed after the current thread function task, so NSLog always executes first

Example 11:

/**
 Variable Array Thread Insecurity Resolution
 */
- (void)demo3{
    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(0, 0);
    //The answer is uncertain
    
    //dispatch_queue_t concurrentQueue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT); Answer 1000
    for (int i = 0; i<1000; i++) {
        dispatch_async(concurrentQueue, ^{
            NSString *imageName = [NSString stringWithFormat:@"%d.jpg", (i % 10)];
            NSURL *url = [[NSBundle mainBundle] URLForResource:imageName withExtension:nil];
            NSData *data = [NSData dataWithContentsOfURL:url];
            UIImage *image = [UIImage imageWithData:data];
            
            dispatch_barrier_async(concurrentQueue , ^{
                [self.mArray addObject:image];
            });
        });
    }
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSLog(@"Number of arrays:%zd",self.mArray.count);
}

Parsing: A raster function with a concurrent queue can intercept asynchronous concurrency, ensure thread safety in the process of inserting data into an array, insert data sequentially, but the raster function can only work on custom queues, system queues are not available, so the answer is not sure. If it is a custom concurrent queue, the number of arrays is 1000

This also reflects the limitations of the use of the fence function, which can only intercept the same queue. If different queues do not work, for example, when we use the methods of some third-party libraries, you do not know what queue they use at the bottom, and naturally you cannot use the fence function to handle it.

Example 12:

- (void)demo2{
    dispatch_queue_t concurrentQueue = dispatch_queue_create("cooci", DISPATCH_QUEUE_CONCURRENT);
        dispatch_queue_t concurrentQueue1 = dispatch_queue_create("kc", DISPATCH_QUEUE_CONCURRENT);

//    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(0, 0);
    // Here's the amount!
    /* 1.Asynchronous function */
    dispatch_async(concurrentQueue, ^{
        NSLog(@"1");
    });
    
    dispatch_async(concurrentQueue, ^{
        sleep(1);
        NSLog(@"2");
    });
    /* 2. Fence function */ // - dispatch_barrier_sync
    dispatch_barrier_sync(concurrentQueue, ^{
        NSLog(@"----%@-----",[NSThread currentThread]);
    });
    /* 3. Asynchronous function */
    dispatch_async(concurrentQueue, ^{
        NSLog(@"3");
    });
    // 4
    NSLog(@"4");
 
}

Resolution: Synchronization + concurrent queue of a raster function can still play a synchronizing role, but it will block the current thread, that is, 3 has to wait for 1 and 2 to finish executing, and the task of the raster function is finished before it can be executed, and 4 has to wait for the raster function to finish executing, so 12 randomly precedes 34 randomly

Example 13:

    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_semaphore_t sem = dispatch_semaphore_create(0);
    
    dispatch_async(queue, ^{
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); // wait for
        sleep(2);
        NSLog(@"Execute Task 1");
        NSLog(@"Task 1 Completed");
    });
    
    //Task 2
    dispatch_async(queue, ^{
        sleep(2);

        NSLog(@"Execute Task 2");
        NSLog(@"Task 2 Completed");
        dispatch_semaphore_signal(sem); // To signal to
    });
    
    //Task 3
    dispatch_async(queue, ^{
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
        
        NSLog(@"Execute Task 3");
        NSLog(@"Task 3 Completed");
        dispatch_semaphore_signal(sem);
    });

Parsing: Initial semaphore 0, global concurrent queue asynchronous function, theoretically concurrent execution, but initial semaphore 0, so Task 1 and Task 3 are waiting. When Task 2 is finished, send a signal, wait for acceptance first, so Task 1 is probably the first to execute, Task 3 is not executed

Example 14:

    dispatch_queue_t queue = dispatch_queue_create("com.ycx.cn", DISPATCH_QUEUE_CONCURRENT);
    dispatch_semaphore_t sem = dispatch_semaphore_create(0);

    //Task 1
    dispatch_async(queue, ^{
        sleep(10);
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); // wait for
        NSLog(@"Execute Task 1");
        NSLog(@"Task 1 Completed");
    });
    
    //Task 2
    dispatch_async(queue, ^{
        NSLog(@"Execute Task 2");
        NSLog(@"Task 2 Completed");
        dispatch_semaphore_signal(sem); // To signal to
    });
    
    //Task 3
    dispatch_async(queue, ^{
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
        NSLog(@"Execute Task 3");
        NSLog(@"Task 3 Completed");
        dispatch_semaphore_signal(sem);
    });


    //Task 4
    dispatch_async(queue, ^{
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
        NSLog(@"Execute Task 4");
        NSLog(@"Task 4 Completed");
        dispatch_semaphore_signal(sem);
    });

Parsing: Task 2 executes first undoubtedly, Task 3 and 4 will precede 1 because a card thread will wait 10 seconds later than 3 and 4, and 3 and 4 will send a signal when executed, while Task 1 will execute because the mechanism of signal, although waiting for 10 seconds, will receive the semaphore now or 1 as long as the wait signal function is executed.

Example 15:

    dispatch_queue_t queue = dispatch_queue_create("com.ycx.cn", DISPATCH_QUEUE_CONCURRENT);
    dispatch_semaphore_t sem = dispatch_semaphore_create(0);
    dispatch_queue_t queue1 = dispatch_queue_create("ycx", NULL);

    //Task 1
    dispatch_async(queue, ^{
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); // wait for
        sleep(2);
        NSLog(@"Execute Task 1");
        NSLog(@"Task 1 Completed");
    });
    
    //Task 2
    dispatch_async(queue1, ^{
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
        sleep(2);
        NSLog(@"Execute Task 2");
        NSLog(@"Task 2 Completed");
    });
    
    //Task 3
    dispatch_async(queue1, ^{
        sleep(2);
        NSLog(@"Execute Task 3");
        NSLog(@"Task 3 Completed");
        dispatch_semaphore_signal(sem);
    });
    
    //Task 4
    dispatch_async(queue, ^{
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
        sleep(2);
        NSLog(@"Execute Task 4");
        NSLog(@"Task 4 Completed");
        dispatch_semaphore_signal(sem);
    });

Parsing: First of all, you need to see two custom queues, queue and queue1, one concurrent and one serial. Tasks 2 and 3 are executed in a serial queue. Although they are asynchronous functions, they do not open another thread. Task 2 has a signal waiting to block the thread and cannot be executed, so Task 3 cannot execute and no signal is sent. So no task can be executed

Example 16:
Implement a scene where pictures are loaded asynchronously to ensure that they are fully loaded before being used to ensure data security

First option:

    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("com.ycx.cn", DISPATCH_QUEUE_CONCURRENT);
    
    __weak typeof(self) weakSelf = self;
    
    dispatch_group_async(group, queue, ^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        //Create Dispatch Group
        NSString *logoStr1 = @"https://img1.baidu.com/it/u=1313595101,2257100959&fm=253&fmt=auto&app=120&f=GIF?w=450&h=300";
        NSData *data1 = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr1]];
        UIImage *image1 = [UIImage imageWithData:data1];
        dispatch_barrier_async(queue, ^{
            [strongSelf.mArray addObject:image1];
            NSLog(@"showTime----111111111");
        });
        
    });

    dispatch_group_async(group, queue, ^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        //Create Dispatch Group
        NSString *logoStr2 = @"https://img1.baidu.com/it/u=1313595101,2257100959&fm=253&fmt=auto&app=120&f=GIF?w=450&h=300";
        NSData *data2 = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr2]];
        UIImage *image2 = [UIImage imageWithData:data2];
        dispatch_barrier_async(queue, ^{
            [strongSelf.mArray addObject:image2];
            NSLog(@"showTime----222222222");
        });
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
       NSLog(@"Number of arrays:%ld",self.mArray.count);
    });

Parsing: Common scenarios for dispatching groups to ensure that pictures are loaded asynchronously, processed uniformly, and written safely to arrays using raster functions

Second option:

    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_queue_t queue1 = dispatch_queue_create("com.ycx.cn", DISPATCH_QUEUE_CONCURRENT);
    
    __weak typeof(self) weakSelf = self;
    
    dispatch_group_async(group, queue, ^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        //Create Dispatch Group
        NSString *logoStr1 = @"https://img1.baidu.com/it/u=1313595101,2257100959&fm=253&fmt=auto&app=120&f=GIF?w=450&h=300";
        NSData *data1 = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr1]];
        UIImage *image1 = [UIImage imageWithData:data1];
        dispatch_barrier_sync(queue1, ^{
            [strongSelf.mArray addObject:image1];
            NSLog(@"showTime----111111111");
        });
    });
    
    dispatch_group_async(group, queue, ^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        //Create Dispatch Group
        NSString *logoStr2 = @"https://img1.baidu.com/it/u=1313595101,2257100959&fm=253&fmt=auto&app=120&f=GIF?w=450&h=300";
        NSData *data2 = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr2]];
        UIImage *image2 = [UIImage imageWithData:data2];
        dispatch_barrier_sync(queue1, ^{
            [strongSelf.mArray addObject:image2];
            NSLog(@"showTime----222222222");
        });
    });
    
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    NSLog(@"Number of arrays:%ld",self.mArray.count);

Resolution: dispatch_ Group_ The wait function waits for the task in the previous group to finish executing, and the latter will block the current thread for uniform processing, using dispatch_barrier_sync and concurrent queues to ensure secure data writing, remember to use dispatch_ Barrier_ A sync is a queue that must be created separately. If the same queue is used with the dispatch group, it can cause deadlocks and wait for each other to execute, as follows

Example 17:

    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("com.ycx.cn", DISPATCH_QUEUE_CONCURRENT);
    
    __weak typeof(self) weakSelf = self;
    
    dispatch_group_async(group, queue, ^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        //Create Dispatch Group
        NSString *logoStr1 = @"https://img1.baidu.com/it/u=1313595101,2257100959&fm=253&fmt=auto&app=120&f=GIF?w=450&h=300";
        NSData *data1 = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr1]];
        UIImage *image1 = [UIImage imageWithData:data1];
        dispatch_barrier_sync(queue, ^{
            [strongSelf.mArray addObject:image1];
            NSLog(@"showTime----111111111");
        });
        NSLog(@"showTime----333333");
    });

    dispatch_group_async(group, queue, ^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        //Create Dispatch Group
        NSString *logoStr2 = @"https://img1.baidu.com/it/u=1313595101,2257100959&fm=253&fmt=auto&app=120&f=GIF?w=450&h=300";
        NSData *data2 = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr2]];
        UIImage *image2 = [UIImage imageWithData:data2];
        dispatch_barrier_sync(queue, ^{
            [strongSelf.mArray addObject:image2];
            NSLog(@"showTime----222222222");
        });
        NSLog(@"showTime----444444");
    });

    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    NSLog(@"Number of arrays:%ld",self.mArray.count);

Resolution: Deadlock lies in dispatch_barrier_sync can block the current thread execution, while the fence function must wait until the previous queue-joined task finishes executing before it can execute, and eventually a thread in the queue deadlocks. The group dispatch group task can never finish executing, dispatch_group_wait has been waiting

Example 18:
Next to the third scenario in Exercise 16

    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("com.ycx.cn", DISPATCH_QUEUE_CONCURRENT);
    
    __weak typeof(self) weakSelf = self;
    
    dispatch_group_enter(group);
   
    dispatch_async(queue, ^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        //Create Dispatch Group
        NSString *logoStr1 = @"https://img1.baidu.com/it/u=1313595101,2257100959&fm=253&fmt=auto&app=120&f=GIF?w=450&h=300";
        NSData *data1 = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr1]];
        UIImage *image1 = [UIImage imageWithData:data1];
        dispatch_barrier_async(queue, ^{
            [strongSelf.mArray addObject:image1];
            NSLog(@"showTime----111111111");
            dispatch_group_leave(group);
        });
    });

    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        //Create Dispatch Group
        NSString *logoStr2 = @"https://img1.baidu.com/it/u=1313595101,2257100959&fm=253&fmt=auto&app=120&f=GIF?w=450&h=300";
        NSData *data2 = [NSData dataWithContentsOfURL:[NSURL URLWithString:logoStr2]];
        UIImage *image2 = [UIImage imageWithData:data2];
        dispatch_barrier_async(queue, ^{
            [strongSelf.mArray addObject:image2];
            NSLog(@"showTime----222222222");
            dispatch_group_leave(group);
        });
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
       NSLog(@"Number of arrays:%ld",self.mArray.count);
    });

Resolution: dispatch_group_enter and dispatch_group_leave in and out groups are used together, which is a bit like semaphores, and must be exactly equal in and out groups before dispatch_can be executed Group_ Tasks inside notify

If you find interesting topics in the future, you will continue to include supplements, mainly for better understanding and use of multithreading

So far in today's sharing, the next one will focus on analyzing the underlying source of GCD

Keywords: C iOS github objective-c cocoapods

Added by alan543 on Thu, 23 Dec 2021 17:17:05 +0200