preface
There are several commonly used multithreading apis: NSThread, GCD, NSOperation
GCD and NSOperation, in particular, are very powerful
Threads refer to processes. Here are a few of the differences:
Process: Process is the smallest unit of operation, allocation of resources and independent operation, as well as the smallest unit of program execution. It can be said that it is an independent APP application, and each process has an independent piece of memory, which causes the isolation between various applications
Thread: Thread is the smallest unit of application execution. A process contains multiple threads and is bound to them. Once a thread fails, its process will also fail and crash
Concurrency and parallelism: parallel, n core threads corresponding to n tasks executed at the same time; Concurrency, n threads, more than N tasks, in the form of time slices executed in turn, to achieve a parallel effect
Note: No matter which SET of apis are used to manipulate threads, the attributes of the threads themselves are the same: start, end, main thread, etc
NSThread
NSThread is a multithreading API for OC that is very simple to use
Start a thread
+ (void)detachNewThreadWithBlock:(void (^)(void))block; + (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument; / / implementation as shown in the following NSThread detachNewThreadWithBlock: ^ {NSLog (@ "I is block a thread"); }]; [NSThread detachNewThreadSelector:@selector(selectorThread) toTarget:self withObject:nil]; - (void)selectorThread {NSLog(@" I'm using an open Selector child thread "); }Copy the code
Switch thread running
/ / back to the main thread to run a method - (void) performSelectorOnMainThread: (SEL) aSelector withObject: (nullable id) arg waitUntilDone: (BOOL) wait; / / into the child thread to run a method - (void) performSelectorInBackground aSelector: (SEL) withObject: (arg nullable id); - (void)performSelector:(SEL)aSelector onThread:(NSThread *) THR withObject:(nullable id)arg WaitUntilDone: (BOOL) wait / / back to the main thread running [self performSelectorOnMainThread: @ the selector (selectorThread) withObject: nil waitUntilDone: NO]; / / into the child thread run [self performSelectorInBackground: @ the selector (selectorThread) withObject: nil]; // Switch to the specified thread and execute [self performSelector:@selector(selectorThread) onThread:[NSThread mainThread] withObject:nil waitUntilDone: NO];Copy the code
Thread start, end, and status
@property (readonly, getter=isExecuting) BOOL executing; @property (readonly, getter=isFinished) BOOL finished; @ Property (readonly, getter=isCancelled) BOOL cancelled; @ Property (readonly, getter=isCancelled) + (void)exit; // Force the thread to terminate, but the task may not complete, resulting in memory leaks, etc. - (void)cancel is not recommended. // The thread will not be cancelled immediately, marked as cancelled, and then cancelled after the current task is completed - (void)start; // Start the threadCopy the code
Note that NSThread methods such as exit, cancel, isExecuting, Cancelled, finished, name, isMainThread, etc., are still used in a thread created in any other way
GCD
GCD, Grand Central Dispatch, is a very powerful SET of C-based apis. Here’s how powerful it is
Queue queue
Queues are created and start threads are performed on a queue-based basis, from which tasks are dispatched to one or more threads for execution
The queue is divided into serial queue and concurrent queue, that is, the difference between single-plank bridge and spacious bridge
Note that the queue fetched via dispatch_get_global_queue is also a concurrent queue, but the tasks also include some system tasks, so subsequent operations may not work well here
Dispatch_queue create(" serial queue ", DISPATCH_QUEUE_SERIAL); // serial queue where dispatch_queue_create(" concurrent queue ", DISPATCH_QUEUE_CONCURRENT); Dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // The global queue has other tasks besides its ownCopy the code
Enable sync and Async threads
Note: Asynchronous tasks are executed concurrently only if they are placed in a concurrent queue; In the same serial queue, synchronous start to execute another task, because of resource grab, that is, can not execute properly (e.g. in the main queue to open a serial queue to the main queue, deadlock)
Dispatch_sync (queue, ^{}) dispatch_sync(queue, ^{}); Dispatch_async (queue, ^{}) dispatch_async(queue, ^{}); // this process is deadlocked dispatch_sync(dispatch_get_main_queue(), ^{NSLog(@" I am serial executing tasks in the main thread "); });Copy the code
Apply multiple times
Calling this method repeats the callback of a set number of times, or in an uncertain order if it is placed on a concurrent queue
int a = 10;
dispatch_apply(10, queue, ^(size_t) {
NSLog(@"%d", a++);
});
Copy the code
Group function group
The grouping function divides tasks in a queue into a group and monitors the execution of tasks. When tasks in a group are completed, the callback method of Notifiy is executed
Note: Tasks are placed in a group, and a callback is performed when the task is executed within the group
Dispatch_group_notify: callback after tasks are executed in a monitoring group. As you can see, group observation is not related to the queue
dispatch_group_async:
Dispatch_group_t group = dispatch_group_create(); Add task 1 to the group, 1 Execute dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0). ^{NSLog(@" I started task 1"); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{NSLog(@" I'm done with 1"); }); }); Add task 2 to the group, 2 Run dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0). ^{NSLog(@" I started task 2"); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{NSLog(@" I finished 2"); }); }); // dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@" all tasks performed "); }); log: 2021-03-03 16:41:20.932220+0800 001-- Multithreading [2690:122386] I start task 2 2021-03-03 16:41:20.945339+0800 001-- Multi-thread [2690:122232] Task completed 2021-03-03 16:41:22.030228+0800 [2690:122232] 1 2021-03-03 16:41:22.030369+0800 001-- Multithreading [2690:122232] 2Copy the code
The following shows the execution results of the above tasks. If the tasks are not asynchronous, the listening works normally. If asynchronous time-consuming operations occur, the listening works normally
Thus the Group enter and leave operations are introduced, as shown below
Dispatch_group_t group = dispatch_group_create(); dispatch_group_enter(group); NSLog(@" I started task 1"); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{NSLog(@" I'm done with 1"); dispatch_group_leave(group); }); dispatch_group_enter(group); NSLog(@" I'm starting task 2"); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{NSLog(@" I finished 2"); dispatch_group_leave(group); }); // dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@" all tasks performed "); }); log: 2021-03-03 16:45:39.174988+0800 001-- Multithreading [2743:124474] I have started the task 2 2021-03-03 16:45:40.175255+0800 001-- Multithreading [2743:124474] I have finished 1 2021-03-03 16:45:40.175483+0800 175699+0800 001-- Multithreading [2743:124474] is completeCopy the code
You can see the more flexible use of Enter and leave, and the more flexible use of asynchronous operations (e.g., network requests)
The signal semaphore
Semaphores are special. Before introducing their use, let’s introduce the mechanism
When the semaphore is set to a value less than 0, the current thread is blocked. When the semaphore value is greater than or equal to 0, the current thread is restored to use
Therefore, the use of a semaphore generally controls the number of threads that can access a code block. This is usually set to 1 to ensure that the code block will only be accessed by multiple threads once
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); // create a semaphore. Default is 1 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // Dispatch_semaphore_signal (semaphore); Static id instance = nil static id instance = nil; static id instance = nil; dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // If (! instance) { instance = [[NSObject alloc] init]; } dispatch_semaphore_signal(semaphore); // The signal quantity value increases by 1Copy the code
The barrier fence
The fence function can separate multiple methods in the same queue and execute the tasks behind the fence after all the tasks added before the fence are executed
Note that you need to create your own queue. Since the global queue is global, not only the current task, but also some system tasks, using the fence function can be problematic
Create your own thread dispatch_queue_t queue = dispatch_queue_create(" test use ", DISPATCH_QUEUE_CONCURRENT); Dispatch_async (queue, ^{NSLog(@" execute 1"); }); Dispatch_async (queue, ^{NSLog(@" execute 2"); }); Dispatch_barrier_async (queue, ^{NSLog(@" I am a barrier, I can separate tasks from each other "); }); // dispatch_barrier_sync(queue, ^{// NSLog(@" I am barrier, I can separate tasks "); / /}); Function dispatch_async(queue, ^{NSLog(@" execute 3"); }); Dispatch_async (queue, ^{NSLog(@" 4 done "); }); log: 2021-03-03 17:01:44.980580+0800 001-- Multi-thread [2797:131038] executed 1 2021-03-03 17:01:44.980614+0800 001-- Multi-thread [2797:131037] executed 2 2021-03-03 17:01:44.980832+0800 001-- Multithreading [2797:130880] I am the barrier, I can separate the task before and after the execution 980989+0800 001-- Multiple threads [2797:131037] executed 4Copy the code
Delay execution after
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{NSLog(@" delay the execution of a task, set the unit to s"); });Copy the code
The timer timer
// The timer can be set to execute the callback interval, just like the timer is used, Advantages more accurate than timer dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); Dispatch_source_set_timer (timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0); Dispatch_source_set_event_handler (timer, ^{NSLog(@" execute a task on time "); }); dispatch_resume(timer);Copy the code
NSOperation
NSOperation is a set of OC-based apis that are not as awkward to use as GCD and are very convenient to use
NSOperation, NSInvocationOperation, and NSBlockOperation
NSInvocationOperation, NSBlockOperation, they both inherit from NSOperation, so they both have the basic functionality of this NSOperation, the only difference is that one of them adds operations by Selector, One is to add operations by way of blocks
Therefore, you can inherit NSOperation to customize NSOperation
NSBlockOperation:
NSBlockOperation *ob = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"%@",[NSThread currentThread]); }]; [ob addExecutionBlock:^{NSLog(@" this is an execution block - %@",[NSThread currentThread]);}]; [ob addExecutionBlock:^{NSLog(@" this is an execution block 2 - %@",[NSThread currentThread]);}]; [ob setCompletionBlock:^{NSLog(@" callback after all done ");}]; [ob start]Copy the code
NSInvocationOperation:
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(handleInvocation:) object:@"123"]; [op start]; - (void)handleInvocation {NSLog(@" This is an invocation block "); }Copy the code
This allows you to create a task code block directly and to execute the task directly using the start method
Note: All tasks (code blocks) added through NSOperation will be added to the concurrent queue. If multiple tasks are added at one time, they will be executed asynchronously and therefore cannot be managed. If they are not added to NSOperationQueue after execution, they will crash
Now let’s look at the basic method of NSOperation
AddDependency: Add a dependency
It can be added repeatedly, which means there can be multiple dependencies. Multiple NSOperation objects can be associated, and the tasks in the dependent objects will be executed first and those in the dependent objects will be executed later
RemoveDependency: Remove dependencies
QueuePriority: Execution priority of the task queue
Start, cancel runs, and cancel the execution of tasks in the queue. When defining NSOperation, you can override these two methods to handle cancellation situations (for example: YYWebImageOperation).
CompletionBlock: callback after all tasks in operation are completed (corresponding to the callback of group_notify)
NSOperationQueue
As you can see from NSOperation, NSOperationQueue is a perfect solution to problems such as thread synchronization
NSOperationQueue is an operation Queue that controls the execution of NSOperations. Nsoperations in a Queue are performed in this Queue according to its specified rules
The queue has a very important attribute maxConcurrentOperationCount, by setting the number can set the number of maximum concurrent NSOperation (every NSOperation a run on the inside)
When maxConcurrentOperationCount parameter is set to 1, then the queue becomes NSOperation serial queue, join the NSOperation can only be executed in accordance with the order (each NSOperaation a task, will serial execution). When the queue setting is greater than 1, a specified number of NSOperations can be concurrent.
Note: NSOperationQueue can only manage NSOperation synchronously. If there are multiple out-of-order tasks in an NSOperation, then the multiple tasks in that NSOperation are still out-of-order
Create an operation object NSBlockOperation *ob = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"%@",[NSThread currentThread]);}]; [ob addExecutionBlock:^{NSLog(@" this is an execution block - %@",[NSThread currentThread]);}]; NSBlockOperation *ob2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"%@",[NSThread currentThread]); }]; [ob2 addExecutionBlock:^{NSLog(@" this is an execution block 2 - %@",[NSThread currentThread]);}]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; queue.maxConcurrentOperationCount = 1; Set the number of queues. When the value is 1, it is the synchronization queue [Queue addOperation:ob]. [queue addOperation:ob2];Copy the code
NSOperationQueue also can run directly add block, and fully accept maxConcurrentOperationCount control
addOperationWithBlock:(void (^)(void))block
Copy the code
suspended
Queues are suspended, that is, stopped running (those that are already running cannot be stopped), and suspended queues can be restarted
CancelAllOperations can cancel all the NSOperation operation in the queue (already in operation can’t stop), because the queue is removed, so it’s no use to start, you need to add the task