Preamble: It’s better to read in order
Concepts section – Processes and threads, tasks and queues
GCD for iOS Multithreading
NSOperation for iOS Multithreading
1. Procedure for using GCD
There are only two steps to use the GCD:
-
Create a queue (serial queue or concurrent queue)
-
The task is appended to the waiting queue of the task, and the system executes the task (synchronously or asynchronously) according to the task type.
1.1 Method of Creating/Obtaining a Queue
// Dispatch_queue_queue = dispatch_queue_create("a. queue ", DISPATCH_QUEUE_SERIAL); // Dispatch_queue_t queue = dispatch_queue_create("b. estQueue", DISPATCH_QUEUE_CONCURRENT); Dispatch_queue_t queue = dispatch_get_main_queue(); /* * The first parameter is to set the priority, default DISPATCH_QUEUE_PRIORITY_DEFAULT=0 */ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);Copy the code
1.2 Method for Creating a Task
{dispatch_sync(queue, ^{// put the task code for synchronous execution}); {dispatch_async(queue, ^{// put the async task code here});Copy the code
2. Deadlock
2.1 the first
In the asynchronous execution + serial queue task, the current serial queue is nested and then executed synchronously.
dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL); Dispatch_async (queue, ^{dispatch_sync(queue, ^{// Execute synchronously + current serial queue // Append task 1 [NSThread sleepForTimeInterval:2]; / / simulation time-consuming operation NSLog (@ "1 - % @", [NSThread currentThread]); // Print the current thread}); });Copy the code
Execution of the above code causes the tasks appending to the serial queue to wait for each other and the tasks existing in the serial queue to wait for each other, blocking the “serial queue” and ultimately causing a deadlock in the thread (subthread) on which the serial queue resides.
2.2 the second
The main queue causes deadlocks as above. Because the main queue is actually a special serial queue
- (void)viewDidLoad { [super viewDidLoad]; NSLog (@ "before the GCD"); Dispatch_sync (dispatch_get_main_queue(), ^{NSLog(@" GCD "); }); NSLog (@ "after the GCD"); }Copy the code
2.3 the third
In the synchronous execution + serial queue task, the current serial queue is nested and then executed synchronously.
dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL); Dispatch_sync (queue, ^{ ^{// Execute synchronously + current serial queue // Append task 1 [NSThread sleepForTimeInterval:2]; / / simulation time-consuming operation NSLog (@ "1 - % @", [NSThread currentThread]); // Print the current thread}); });Copy the code
3. Simple use of GCD
3.1 Processing Time-consuming Operations
The time-consuming operation is done by the thread, which needs to go back to the main thread and refresh the UI
/** * inter-thread communication */ - (void)communication {// Get global concurrent queue dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); Dispatch_queue_t mainQueue = dispatch_get_main_queue(); Dispatch_async (queue, ^{// Add task 1 [NSThread sleepForTimeInterval:2]; / / simulation time-consuming operation NSLog (@ "1 - % @", [NSThread currentThread]); Dispatch_async (mainQueue, ^{// append tasks to the main thread [NSThread sleepForTimeInterval:2]; / / simulation time-consuming operation NSLog (@ "2 - % @", [NSThread currentThread]); // Print the current thread}); }); }Copy the code
3.2 Delaying task execution dispatch_after
The dispatch_After method does not start processing after a specified time, but rather appends tasks to the main queue after a specified time. Strictly speaking, this time is not absolutely accurate, but the dispatch_after method is useful for roughly delaying the execution of tasks.
/** * dispatch_after */ - (void)after {NSLog(@"currentThread-- %@",[NSThread currentThread]); // Print the current thread NSLog(@"asyncMain-- begin"); Dispatch_after (dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), ^{// Append the task code to the main queue asynchronously after 2.0 seconds and start executing NSLog(@"after-- %@",[NSThread currentThread]); // Print the current thread}); }Copy the code
4. GCD advanced application
4.1 Fence Function
Dispatch_barrier_async is a fence function that waits for the previous task to finish before executing the next task.
Simple application of dispatch_barrier_Async
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT); Dispatch_async (queue, ^{// append task 1 [NSThread sleepForTimeInterval:2]; / / simulation time-consuming operation NSLog (@ "1 - % @", [NSThread currentThread]); // Print the current thread}); Dispatch_barrier_async (queue, ^{// add barrier [NSThread sleepForTimeInterval:2]; NSLog(@"barrier-- %@",[NSThread currentThread]); // Print the current thread}); Dispatch_async (queue, ^{// append task 2 [NSThread sleepForTimeInterval:2]; / / simulation time-consuming operation NSLog (@ "2 - % @", [NSThread currentThread]); // Print the current thread});Copy the code
There are two types of fence functions, synchronous dispatch_barrier_sync and asynchronous dispatch_barrier_async
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { dispatch_async(dispatch_queue_create("mmmmmm", DISPATCH_QUEUE_CONCURRENT), ^{ [self logBarrierOrder]; }); } - (void)logBarrierOrder { NSLog(@"%@ >>>>> start ", [NSThread currentThread]); dispatch_queue_t queue = dispatch_queue_create("test.barrier.queue", DISPATCH_QUEUE_CONCURRENT); NSLog(@"% @>>>>> barrier A ", [NSThread currentThread]); dispatch_async(queue, ^{ sleep(5); NSLog (@ % @ "> > > > > task 1", [NSThread currentThread]); }); NSLog(@"% @>>>>> barrier B ", [NSThread currentThread]); dispatch_async(queue, ^{ sleep(3); NSLog (@ % @ "> > > > > task 2", [NSThread currentThread]); }); NSLog(@"% @>>>>> barrier C ", [NSThread currentThread]); dispatch_async(queue, ^{ sleep(1); NSLog (@ % @ "> > > > > task 3", [NSThread currentThread]); }); NSLog(@"% @>>>>> barrier D ", [NSThread currentThread]); dispatch_barrier_sync(queue, ^{ sleep(7); NSLog(@"%@ ++++++ barrier ++++++ ", [NSThread currentThread]); }); NSLog(@"% @>>>>> barrier E ", [NSThread currentThread]); Dispatch_async (queue, ^{NSLog(@"% @>>>>> task 4 ", [NSThread currentThread]); }); NSLog(@"% @>>>>> barrier F ", [NSThread currentThread]); dispatch_async(queue, ^{ sleep(10); NSLog (@ % @ "> > > > > 5" task, [NSThread currentThread]); }); NSLog(@"% @>>>>> barrier G ", [NSThread currentThread]); Dispatch_async (queue, ^{NSLog(@"% @>>>>> task 6 ", [NSThread currentThread]); }); NSLog(@"%@ >>>>> end ", [NSThread currentThread]); } / / = = = = = = = = = = = = = = = = = = dispatch_barrier_sync execution results following = = = = = = = = = = = = = = = = = = / / dispatch_barrier_sync execution results > > > > > start > > > > > In front of the barrier in front of A > > > > > the barrier B > > > > > the barrier in front of the C > > > > > in front of the barrier D > > > > > task 3 > > > > > task 2 > > > > > task 1 + + + + + + the barrier + + + + + + > > > > > the barrier behind E > > > > > behind the barrier F > > > > > task 4 > > > > > behind the barrier G > > > > > end > > > > > task 6 > > > > > task 5 / / = = = = = = = = = = = = = = = = = = dispatch_barrier_sync execution results above = = = = = = = = = = = = = = = = = = / / / / = = = = = = = = = = = = = = = = = = dispatch_barrier_async execution results following = = = = = = = = = = = = = = = = = = / / > > > > > start > > > > > the barrier in front of A > > > > > B in front of the barrier > > > > > the barrier in front of the C > > > > > in front of the barrier D > > > > > the barrier behind E > > > > > behind the barrier F > > > > > behind the barrier G > > > > > end > > > > > task 3 > > > > > task 2 > > > > > task 1 + + + + + + + + + + + + barrier > > > > > task 4 > > > > > task 6 > > > > > task 5 / / = = = = = = = = = = = = = = = = = = dispatch_barrier_async execution results above = = = = = = = = = = = = = = = = = = / /Copy the code
- In common
- Wait for tasks 1,2, and 3 to complete, then execute the barrier, execute the barrier, and finally execute tasks 4,5, and 6
- The difference between
- Dispatch_barrier_sync will wait for its own barrier to finish before executing the tasks (E, F, G) and then executing the tasks (4,5,6).
- Dispatch_barrier_async inserts its own barrier task after the queue. Instead of waiting for its own task to finish, it continues to insert subsequent tasks (E, F, G) into the queue
Fence function note:
- Use custom concurrent queues as much as possible. Using global queues does not function as a fence function. When using a global queue, blocking the global queue may cause the rest of the system to block the global queue and cause a crash (you are not the only one using this queue).
- The fence function can only control the same concurrent queue
- Similarities and differences between dispatch_barrier_Async and dispatch_barrier_sync: Both prevent queue execution (in this thread), but dispatch_barrier_sync also prevents thread execution
- Dispatch_barrier_sync is executed in the main thread and dispatch_barrier_Async is executed in the child thread.
- Dispatch_barrier_sync and dispatch_barrier_Async function the same in a serial queue. Dispatch_async will create a new thread, if it is a serial queue, then only one thread will be started and tasks will be executed in the order they were added.
4.2 One-time code (executed only once): dispatch_once
/** * dispatch_token */ - (void) {static dispatch_once_t onceToken; Dispatch_once (&onceToken, ^{// execute code only once(thread safe by default)}); }Copy the code
SharedInstance {static TMProgressHUD *sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstance = [self new]; }); return sharedInstance; }Copy the code
4.3 GCD fast iteration method: dispatch_apply
The dispatch_apply function is the API associated with the dispatch_sync function and Dispatch Group. This function appends the specified Block to the specified Dispatch Queue the specified number of times and waits for all processing to finish.
-
If dispatch_apply is used in a serial queue, it is executed synchronously in the same order as the for loop. But that doesn’t make sense to iterate quickly.
-
If you are executing asynchronously on a concurrent queue, the for loop works by traversing it sequentially. Dispatch_apply can be traversed simultaneously (asynchronously) (out of order) in multiple threads.
-
Dispatch_apply waits for all tasks to complete, either in a serial queue or in a concurrent queue, just like a synchronous operation or the dispatch_group_WAIT method in a queue group.
-
When dispatch_apply is added to the main queue (because dispatch_apply is essentially the API associated with the dispatch_sync function and Dispatch Group)
dispatch_apply(6, dispatch_get_main_queue(), ^(size_t index){});
* dispatch_get_global_queue is a global queue with concurrent queues * in out-of-order traversal, but apply-- end is executed at the end */ - (void)apply { dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); NSLog(@"apply---begin"); dispatch_apply(6, queue, ^(size_t index) { NSLog(@"%zd---%@",index, [NSThread currentThread]); }); NSLog(@"apply---end"); }Copy the code
Because tasks are executed asynchronously in a concurrent queue, the execution time of each task varies, as does the order in which it is executed. But apply- end must be executed at the end. This is because the dispatch_apply method waits for all tasks to complete.
4.4 set the queue
dispatch_group_notify
Listen for the completion status of tasks in the group. When all tasks are completed, add tasks to the group and execute them.
/** * queue group dispatch_group_notify */ - (void)groupNotify {NSLog(@"currentThread-- %@",[NSThread currentThread]); // Print the current thread NSLog(@"group-- begin"); dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{// append task 1 [NSThread sleepForTimeInterval:2]; / / simulation time-consuming operation NSLog (@ "1 - % @", [NSThread currentThread]); // Print the current thread}); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{// append task 2 [NSThread sleepForTimeInterval:2]; / / simulation time-consuming operation NSLog (@ "2 - % @", [NSThread currentThread]); // Print the current thread}); Dispatch_group_notify (group, dispatch_get_main_queue(), ^{// After asynchronous tasks 1 and 2 are executed, NSThread sleepForTimeInterval:2 NSThread sleepForTimeInterval:2 / / simulation time-consuming operation NSLog (@ "3 - % @", [NSThread currentThread]); // Print the current thread NSLog(@"group-- end"); }); }Copy the code
dispatch_group_wait
Suspend the current thread (block the current thread) and wait for the completion of the task in the specified group before continuing execution.
/** * queue group dispatch_group_wait */ - (void)groupWait {NSLog(@"currentThread-- %@",[NSThread currentThread]); // Print the current thread NSLog(@"group-- begin"); dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{// append task 1 [NSThread sleepForTimeInterval:2]; / / simulation time-consuming operation NSLog (@ "1 - % @", [NSThread currentThread]); // Print the current thread}); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{// append task 2 [NSThread sleepForTimeInterval:2]; / / simulation time-consuming operation NSLog (@ "2 - % @", [NSThread currentThread]); // Print the current thread}); Dispatch_group_wait (group, DISPATCH_TIME_FOREVER); NSLog(@"group---end"); }Copy the code
Dispatch_group_enter, dispatch_group_leave
- Dispatch_group_enter Indicates that a task is added to a group and executed once. This is equivalent to the number of unfinished tasks in the group +1
- Dispatch_group_leave indicates that a task has left the group and is executed once. This is equivalent to the number of unfinished tasks in the group -1.
- Only when the number of unfinished tasks in the dispatch_group_wait group is 0, the block of dispatch_group_WAIT is unblocked and the tasks added to dispatch_group_notify are executed.
/** * Queue group dispatch_group_Enter, dispatch_group_leave */ - (void)groupEnterAndLeave { NSLog(@"currentThread---%@",[NSThread currentThread]); // Print the current thread NSLog(@"group-- begin"); dispatch_group_t group = dispatch_group_create(); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_group_enter(group); Dispatch_async (queue, ^{// append task 1 [NSThread sleepForTimeInterval:2]; / / simulation time-consuming operation NSLog (@ "1 - % @", [NSThread currentThread]); Dispatch_group_leave (group); }); dispatch_group_enter(group); Dispatch_async (queue, ^{// append task 2 [NSThread sleepForTimeInterval:2]; / / simulation time-consuming operation NSLog (@ "2 - % @", [NSThread currentThread]); Dispatch_group_leave (group); }); [NSThread sleepForTimeInterval:2] [NSThread sleepForTimeInterval:2] [NSThread sleepForTimeInterval:2]; / / simulation time-consuming operation NSLog (@ "3 - % @", [NSThread currentThread]); // Print the current thread NSLog(@"group-- end"); }); }Copy the code
4.5 GCD semaphore: dispatch_semaphore
-
Three functions related to dispatch_semaphore
-
// Create the semaphore. Parameter: initial value of the semaphore. NULL dispatch_semaphore_t dispatch_semaphore_CREATE (long value);
-
If the semaphore of a signal is zero, then the current thread will be blocked until the semaphore is greater than zero or the input time value has passed. */ long dispatch_semaphore_WAIT (dispatch_semaphore_t dSEMa, dispatch_time_t TIMEOUT); */ long dispatch_semaphore_t dSEMA;
-
Dispatch_semaphore_signal (dispatch_semaphore_t dsema);
-
-
A signal and a wait usually come in pairs.
(1) Use dispatch_semaphore to control the execution of one asynchronous method before another
-(void)dispatchSignal{ dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // Dispatch_async (quene, ^{NSLog(@"run task 1")); sleep(2); NSLog(@"complete task 1"); dispatch_semaphore_signal(semaphore); }); dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // Dispatch_async (quene, ^{NSLog(@"run task 2")); sleep(1); NSLog(@"complete task 2"); dispatch_semaphore_signal(semaphore); }); dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); Dispatch_async (quene, ^{NSLog(@"run task 3")); sleep(1); NSLog(@"complete task 3"); dispatch_semaphore_signal(semaphore); }); NSLog(@"-------->end"); }Copy the code
The execution result
run task 1
complete task 1
run task 2
complete task 2
run task 3
complete task 3
——–>end
(2) Use the value of dispatch_semaphore_CREATE to control the number of concurrent threads
/* the value of "dispatch_semaphore_create" indicates that a maximum of several resources can be accessed. If the value of "dispatch_semaphore_create" is set to "2", two threads are executed before the next one is executed. If the signal value is set to 3, there is no limit to thread execution in this method because there are only three threads. */ -(void)dispatchSignal{ dispatch_semaphore_t semaphore = dispatch_semaphore_create(2); dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // Task 1 dispatch_async(quene, ^{dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"run task 1"); sleep(2); NSLog(@"complete task 1"); dispatch_semaphore_signal(semaphore); }); // Task 2 dispatch_async(quene, ^{dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"run task 2"); sleep(1); NSLog(@"complete task 2"); dispatch_semaphore_signal(semaphore); }); // Task 3 dispatch_async(quene, ^{dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"run task 3"); sleep(1); NSLog(@"complete task 3"); dispatch_semaphore_signal(semaphore); }); }Copy the code
The execution result
run task 1
run task 2
complete task 2
complete task 1
run task 3
complete task 3