In the previous chapter, we learned some of the basics of multithreading. This chapter will explain in peacetime development, the most commonly used GCD some knowledge
Portal series:
☞ iOS Basics – Principles of Multithreading
The GCD introduction
define
The whole process of GCD is Grand Central Dispatch, implemented by C language, which is a solution proposed by Apple for multi-core parallel computing. CGD will automatically utilize more CPU cores and automatically manage the life cycle of threads. Programmers only need to tell GCD the tasks that need to be performed, without writing any code for thread management. GCD is also the most used multi-threading technology on iOS.
advantage
GCD
Is apple’s solution to multi-core parallel computingGCD
– More CPU cores are automatically utilized (e.g., dual-core, quad-core)GCD
Automatically manages the thread lifecycle (thread creation, task scheduling, thread destruction)- The programmer just needs to tell
GCD
What do you want to do without writing any thread management code
The GCD use
The USAGE API of GCD is relatively simple, mainly divided into synchronous and asynchronous execution. We have already covered the concepts in the previous chapter, so we will not repeat them.
Synchronous execution API
//queue: queue block: task
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
Copy the code
Asynchronous execution API
//queue: queue block: task
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
Copy the code
We found that when using GCD, we need to pass in two parameters. “Dispatch_queue_t” indicates the queue type to be added, “dispatch_block_t” indicates the task to be executed.
dispatch_block_t
task
As you can see by looking at its definition, this argument is actually a block callback with no arguments, used to perform the task.
typedef void (^dispatch_block_t)(void);
Copy the code
dispatch_queue_t
The queue
The dispatch_queue_t parameter indicates the queue type. As we know from the previous chapter, queues (FIFO) are mainly divided into four types in iOS:
- The home side columns (
main_queue
) : serial queue created by the system.- Obtaining method:
dispatch_get_main_queue()
- Obtaining method:
- Global queue (
global_queue
) : a concurrent queue created by the system- Obtaining method:
dispatch_get_global_queue(long identifier, unsigned long flags);
- Obtaining method:
- Serial queue (
Serial Dispatch Queue
) : a custom serial queue- Obtaining method:
Dispatch_queue_create (@" queue name ",DISPATCH_QUEUE_SERIAL)
- Obtaining method:
- Concurrent queue (
Concurrent Dispatch Queue
) : custom concurrent queue- Obtaining method:
Dispatch_queue_create (@" queue name ",DISPATCH_QUEUE_CONCURRENT);
- Obtaining method:
The global queue will get different queues according to different input parameters. In daily development, we usually enter parameter 0 to get the main concurrent queue.
Custom queues are created by calling dispatch_queue_CREATE (const char *_Nullable label,dispatch_queue_attr_t _Nullable attr). The two parameters are the custom queue name and queue type. The serial queue DISPATCH_QUEUE_SERIAL is NULL defined by the macro, so NULL is also represented as a serial queue.
Now, what happens if we look at the two execution modes, the three queues are paired together
Synchronous + (main queue or custom serial queue)
The relevant codes are as follows:
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(Task 1: % @ "@"[NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(Task 2 "@"[NSThread currentThread]);
});
NSLog(Task 3: % @ "@"[NSThread currentThread]);
}
Copy the code
The results
After printing out task 1, the program deadlock crashed
Why do these results occur?
The main thread performs task 1, dispatch_get_main_queue() synchronizes task 2 with (dispatch_sync), then performs task 3.
The feature of queue is FIFO. Task viewDidLoad already exists in the main queue. If you add task 2 to the main queue, you need to complete task 2 in viewDidLoad before task 2 can be executed. But in order to finish viewDidLoad, you have to do task 2 and task 3 in viewDidLoad. This creates a deadlock.
Asynchronous + (main queue or custom serial queue)
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(Task 1: % @ "@"[NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
NSLog(Task 2: % @ "@"[NSThread currentThread]);
});
NSLog(Task 3: % @ "@"[NSThread currentThread]);
}
Copy the code
The results
We can see that when the main queue is obtained, it is executed asynchronously without causing the program to crash.
After task 1 is executed by the main thread, task 2 needs to be executed asynchronously (dispatch_async). Dispatch_async does not require simultaneous execution of tasks on the current thread immediately, i.e., does not block; So the main thread then performs task 3, and finally asynchronously performs task 2.
Synchronous + (global queue or custom concurrent queue)
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t queue = dispatch_get_global_queue(0.0);
dispatch_sync(queue, ^{
for (int i = 0; i<3; i++) {
NSLog(Task 1: %@[NSThreadcurrentThread]); }});dispatch_sync(queue, ^{
for (int i = 0; i<3; i++) {
NSLog(@" Task 2: %@"[NSThreadcurrentThread]); }}); }Copy the code
The results
According to the order
A task was executed in the main thread, no new thread was opened
Asynchronous + (global queue or custom concurrent queue)
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t queue = dispatch_get_global_queue(0.0);
dispatch_async(queue, ^{
for (int i = 0; i<3; i++) {
NSLog(Task 1: %@[NSThreadcurrentThread]); }});dispatch_async(queue, ^{
for (int i = 0; i<3; i++) {
NSLog(@" Task 2: %@"[NSThreadcurrentThread]); }}); }Copy the code
Running results:
It can be seen that task 1 and task 2 are interleaved. Task 2 is executed after task 1 is executed. And the thread number indicates that a new thread has indeed been started. Note Asynchronous execution has the ability to start new threads.
However, we can see from the above print result of asynchronous + main queue, in the main queue, the asynchronous execution does not start a new thread, but still the same main thread. Note If the thread is executed asynchronously in the main queue, no new thread is started
The reason is that the main queue is a serial queue, and one task must be executed before another. Asynchronous execution does not cause congestion, but the main queue still has to go step by step.
Nesting of the same serial queue
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@ "task 1");
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(Task 2 "@");
dispatch_sync(queue, ^{
NSLog(Task 3 "@");
});
NSLog(Task 4 "@");
});
NSLog(5 "@" task);
}
Copy the code
The results
Task 1, task 5, task 2, and then it crashes
Deadlock crashes are caused by the same reason as synchronous + serial queues above. Because the main queue is serial, the code must be executed from top to bottom.
After task 1 is printed, dispatch_async is asynchronous, it does not block the thread, but it is added to a customized serial queue, so task 5 is executed, then the logic in the asynchronous code block is executed, task 2 is printed, then the dispatch_sync code block is executed, but because it is synchronous, The dispatch_async task has not been completed in the serial queue, and then dispatch_sync blocks the current thread waiting for task 3 to execute, causing mutual waiting, so the deadlock crashes
Nesting of different serial queues
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@ "task 1");
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("myQueue2", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(Task 2 "@");
dispatch_sync(queue2, ^{
NSLog(Task 3 "@");
});
NSLog(Task 4 "@");
});
NSLog(5 "@" task);
}
Copy the code
The results
Task 1, Task 5, Task 2, task 3, task 4
By running it we can see that when nested execution is performed asynchronously on different serial queues, it does not cause deadlock crashes. I’m going to execute the code in serial order. This does not cause a deadlock wait because dispatch_sync blocks another thread rather than the current one.
Nesting of the same concurrent queue
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@ "task 1");
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(Task 2 "@");
dispatch_sync(queue, ^{
NSLog(Task 3 "@");
});
NSLog(Task 4 "@");
});
NSLog(5 "@" task);
}
Copy the code
Running results:
Task 1, Task 5, Task 2, task 3, task 4
After the main thread dispatch_async performs task 1, it needs to perform task 2 asynchronously. So execute task 5 of the main thread first, then task 2; Dispatch_sync then needs to be synchronized in the concurrent queue for task 3, so it blocks, waits for it to complete, and then executes task 4 in the concurrent queue.
Different concurrent queues are nested
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@ "task 1");
dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("myQueue2", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(Task 2 "@");
dispatch_sync(queue2, ^{
NSLog(Task 3 "@");
});
NSLog(Task 4 "@");
});
NSLog(5 "@" task);
}
Copy the code
Running results:
Task 1, Task 5, Task 2, task 3, task 4
This code is analyzed in a similar way to the nesting of different serial queues, creating a new queue so it does not block the current queue.
conclusion
- Synchronous execution does not open up threads, and the code executes sequentially, causing congestion
- Synchronous execution on the same serial queue causes a deadlock wait
- Asynchronous execution has the ability to open up threads. In concurrent queue execution, the order of execution is uncertain. In serial queue execution, the order of execution is sequential, and no new threads are opened in the main queue
The resources
Apple multithreaded open source code
Exploring the underlying principles of iOS – the nature of multithreading