[OC learning notes] learn about GCD threads

Posted by Miichael on Sun, 20 Feb 2022 09:53:30 +0100

1, Learn some basic knowledge

(1) Queue:

Queue queues are divided into the following types:

1. Global queue:

dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

2. Main queue:

dispatch_get_main_queue()

3. User created serial queue:

dispatch_queue_create("com.yuna.com", DISPATCH_QUEUE_SERIAL/NULL);

4. User created parallel queues:

dispatch_queue_create("com.yuna.com", DISPATCH_QUEUE_CONCURRENT);

(2) Synchronous and asynchronous

1. Synchronization

dispatch_sync(queue,block): synchronization queue, dispatch_ The sync function will not return immediately. It will block the current thread and put the block on the specified queue for execution. It will not return until the synchronous execution of the block is completed

2. Asynchronous

dispatch_async(queue,block): asynchronous queue, dispatch_ The async function will return immediately, and the block will be placed in the specified queue for execution. Whether the block is parallel or serial only depends on the definition of queue

(3) Look at the combination

/*
 Serial queue + synchronization task: new threads are not started, and tasks are completed one by one
 */
- (void)serialSync {
    dispatch_queue_t queue = dispatch_queue_create("com.pi2e.com", DISPATCH_QUEUE_SERIAL);
    NSLog(@"Serial queue + synchronization----%@",[NSThread currentThread]);
//    dispatch_queue_t queue = dispatch_queue_create("com.pi2e.com", NULL);
    //Add synchronization task to queue
    dispatch_sync(queue, ^{
        for (int  i = 0; i < 5; i++) {
            NSLog(@"---1--%@", [NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int  i = 0; i < 5; i++) {
            NSLog(@"---2--%@", [NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"---3--%@", [NSThread currentThread]);
        }
    });
}

/*
 Serial queue + asynchronous task: start a new thread, and the tasks are completed one by one
 */
- (void)serialAsync {
    dispatch_queue_t queue = dispatch_queue_create("com.pi2e.com", DISPATCH_QUEUE_SERIAL);
    //    dispatch_queue_t queue = dispatch_queue_create("com.pi2e.com", NULL);
    NSLog(@"Serial queue + asynchronous----%@",[NSThread currentThread]);
    //Add asynchronous task to queue
    dispatch_async(queue, ^{
        for(int  i = 0; i < 5;i++) {
            NSLog(@"---1--%@", [NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for(int  i = 0; i < 5; i++) {
            NSLog(@"---2--%@", [NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for(int  i = 0; i < 5; i++) {
            NSLog(@"---3--%@", [NSThread currentThread]);
        }
    });
}

/*
 Concurrent queue + synchronization task: new threads are not started, but tasks are executed one by one
 */
- (void)createConCurrentQueueSyn {
    //Create concurrent queue
    dispatch_queue_t queue = dispatch_queue_create("com.pi2e.com", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"Concurrent queue + synchronization----%@",[NSThread currentThread]);
    //Add synchronization task to queue
    dispatch_sync(queue, ^{
        for (int  i = 0 ; i < 5;i++){
            NSLog(@"---1--%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int  i=0 ; i < 5;i++) {
            NSLog(@"---2--%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int  i=0 ; i < 5;i++){
            NSLog(@"---3--%@",[NSThread currentThread]);
        }
    });
}

/*
 Concurrent queue + asynchronous task: start a new thread, and the task is concurrent
*/

- (void)createConCurrentQueueAsyn {
    
    dispatch_queue_t queue = dispatch_queue_create("com.pi2e.com", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"Concurrent queue + asynchronous----%@", [NSThread currentThread]);
    //Add asynchronous task to queue
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"--1--%@", [NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"--2--%@", [NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"--3--%@", [NSThread currentThread]);
        }
    });
}

/*
 Global queue + synchronization task:, no new thread is started, and the tasks are executed one by one
 */
- (void)GlabolSyc {
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    NSLog(@"Global queue + synchronization----%@",[NSThread currentThread]);
    //Add synchronization task to queue
    dispatch_sync(queue, ^{
        for (int i=0 ; i < 5; i++) {
            NSLog(@"---1--%@", [NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int i=0 ; i < 5; i++) {
            NSLog(@"---2--%@", [NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int i=0 ; i < 5; i++) {
            NSLog(@"---3--%@", [NSThread currentThread]);
        }
    });

}

/*
 Global + asynchronous: start a new thread, and the task is concurrent
 */

- (void)GlobalAsync {
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
     NSLog(@"Global queue + asynchronous----%@", [NSThread currentThread]);
    //Add asynchronous task to queue
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"--1--%@", [NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"--2--%@", [NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"--3--%@", [NSThread currentThread]);
        }
    });
}

/*
 Main queue (serial) + synchronization: interface stuck
 Adding synchronization tasks to the main queue will cause deadlock
 */
//dispatch_sync refers to the synchronous execution of task block s in the specified thread queue,
//dispatch_sync has the feature of waiting for the end of block execution before callback;
- (void)mainthreadSync {
    dispatch_queue_t queue = dispatch_get_main_queue();
    NSLog(@"Main queue (serial)+synchronization----%@",[NSThread currentThread]);
    //Add synchronization task to queue
    dispatch_sync(queue, ^{
        for (int i = 0; i < 10; i++) {
            NSLog(@"--1--%@", [NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int i = 0; i < 10; i++) {
            NSLog(@"--2--%@", [NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for (int i = 0; i < 10; i++) {
            NSLog(@"--3--%@", [NSThread currentThread]);
        }
    });
}
/*
 Main queue (serial) + asynchronous: no new thread is created, and tasks are completed one by one {all executed in the main thread}
 */
- (void)mainthreadAsync {
    dispatch_queue_t queue = dispatch_get_main_queue();
    NSLog(@"Main queue (serial)+asynchronous----%@",[NSThread currentThread]);
    //Add asynchronous task to queue
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"--1--%@", [NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"--2--%@", [NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            NSLog(@"--3--%@", [NSThread currentThread]);
        }
    });
}

(4) Other

Analysis of NSNotificationCenter

- (void)viewDidLoad {
    [super viewDidLoad];
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(passValue:) name:@"PassValue" object:nil];
    [self sentValue];
    NSLog(@"Notification assignment completed");
}
- (void)sentValue {
    NSLog(@"Send notification");
    NSDictionary *dict = @{@"myValue":@"Billy Notification value transmission"};
    [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:@"PassValue" object:nil userInfo:dict]];
}

- (void)passValue:(NSNotification *)text{
    NSString *valueStr = text.userInfo[@"myValue"];
    NSLog(@"Received value:%@",valueStr);
    sleep(3);
}


analysis:
Through the printing time, we can see that after we send the notification, the observer sleeps for 3 seconds after receiving the value, and the program will continue to execute, that is, the process is synchronous; It is designed to be synchronous, probably considering that a notification may have multiple listeners. The synchronous method can ensure that all observers can respond to the notification without omission.

Change to asynchronous:

- (void)sentValue {
    NSLog(@"Send notification");
    NSDictionary *dict = @{@"myValue":@"Billy Notification value transmission"};
    dispatch_async(dispatch_get_main_queue(), ^{
        [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:@"PassValue" object:nil userInfo:dict]];
    });
}

2, Look at the deadlock

First understand several concepts:

(1) Serial queue:

Tasks are added to a queue in sequence and executed in sequence. The commonly used serial queues include the main queue and the general serial queue.

(2) dispatch_sync():

Synchronous tasks, that is, after the previous task is executed, the subsequent tasks will start to execute. Otherwise, the subsequent tasks are in a waiting state.

(3) Causes of Deadlock:

In the same serial queue, if an execution function in the main queue is not completed, it happens that the execution function passes dispatch_sync() adds a synchronization task, and a deadlock occurs. Adding synchronization tasks to the main queue will cause deadlock. That is: the execution function in the main queue is currently being executed as the first task. Only after the function is executed can the first task be executed and the result be returned. At this time, the subsequent tasks will be executed downward. If the dispatch is passed in the execution function at this time_ Sync() synchronously adds a newTask, so the execution function can only be executed downward after the newly added newTask is executed, that is, the call of the execution function is completed, that is, the execution of the first task is completed; As the second task added synchronously, newTask will not be executed until the first task, that is, the execution function, is executed. In this way, there is a situation of waiting for each other. The two tasks can never be executed, that is, deadlock.

(4) Example:

- (void)viewDidLoad {
	[super viewDidLoad];
  	dispatch_sync(dispatch_get_main_queue(), ^{
      	NSLog(@"newTask");
 	});

Synchronization is executed immediately for the task. When the task is put into the main queue, it will execute immediately. Only after the task is executed, viewDidLoad will continue to execute downward. The viewDidLoad and the task are on the main queue. Due to the first in first out principle of the queue, the task can not continue until the viewDidLoad is executed. The viewDidLoad and the task form a mutual circular wait, resulting in a deadlock. To avoid this deadlock, you can change synchronization to asynchronous dispatch_async, or dispatch_get_main_queue can be replaced with other serial or parallel queues. Therefore, do not use dispatch_sync() pushes the block into the current queue.

Topics: iOS Design Pattern objective-c OC