At the beginning

  • This is a sub-chapter. For the whole article, see the Learning Diary of Zero-based iOS development
  • Swift implementation of GCD

multithreading

The actual use

  • The network access operation may fail. After the operation succeeds, the system returns to the main thread for data processing or display
  • Processing time-consuming operations, and then return to the main thread
  • My understanding is that an app runs likeAvengersThe main world, and the time consuming operation isThe scene that the ancient Mage showed the HulkA functioning space-time was created by borrowing a few gemsThe new time and space, that is, the child thread, after execution, returns the gem to its original owner and returns to the main world, while the app itself executes at its own pace in this process
  • Often the work of network access or data delivery is placed in the child thread and then returned to the main thread for refreshing

Implementation method

  • NSThread
  • GCD
  • NSOpreation

Noun explanation

  • Process, analogy company. An application running on a system in which processes are independent of each other and run in their own dedicated and protected memory space
  • Threads, analogous to employees. A process consists of multiple threads and is the basic execution unit of a process. All execution tasks of a process are executed in the thread
  • The main thread, like the boss. App default thread for executing tasks
  • Synchronous execution, a path, does not have the ability to start new threads
  • Asynchronous execution, multiple paths, and the ability to start new threads
  • Serial queues, one after the other
  • Concurrent queues, executed simultaneously, are only valid for asynchronous functions
  • The main queue, which executes the main queue’s code before executing the main queue’s work, is essentially a serial queue, but it waits for the main queue’s code
  • Thread-safe, where one and only one thread writes to a variable at a time, is thread-safe; Conversely, threads are unsafe
  • Thread synchronization: Thread A and thread B cooperate in execution. When thread A reaches A certain part, it needs to obtain some data from thread B. After B completes execution, thread A obtains data and then executes

NSTread

use

  • The first way
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil]; [thread start]; Thread1. name = @"testThread"; thread2.name = @"thread2"; / / thread priority. QualityOfService = NSQualityOfServiceUserInitiated;Copy the code
  • The second way
[NSThread detachNewThreadSelector:@selector(demo) toTarget:self withObject:nil];
Copy the code
  • The third way
[self performSelectorInBackground:@selector(demo) withObject:nil];
Copy the code
  • Return to main thread execution
UntilDone () {//object incoming parameters //untilDone To carry out the tasks of follow-up [self performSelectorOnMainThread: @ the selector (main) withObject: nil waitUntilDone: YES];Copy the code

Case study – Download web pictures

- (void)loadView{ self.scrollView = [[UIScrollView alloc] initWithFrame:[UIScreen mainScreen].bounds]; self.scrollView.backgroundColor = [UIColor blackColor]; self.view = self.scrollView; self.imageView = [UIImageView new]; [self.scrollView addSubview:self.imageView]; } - (void)viewDidLoad { [super viewDidLoad]; NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(downloadImage) object:nil]; [thread start]; } // downloadImage - (void)downloadImage{NSURL *url = [NSURL URLWithString:@"https://p4.itc.cn/images01/20210306/c866be67263f4c8484228b593315f7c4.jpeg"]; NSData *data = [NSData dataWithContentsOfURL:url]; Uiimage *img = [UIImage imageWithData:data]; Performed on / / / / in the main thread waitUntilDone value is YES wait method performs / / back to the main thread in the child thread [self performSelectorOnMainThread: @ the selector (updataUI:) withObject:img waitUntilDone:YES]; } - (void)updataUI:(UIImage *)img{ self.imageView.image = img; [self.imageView sizeToFit]; self.scrollView.contentSize = img.size; }Copy the code

GCD

combination

  • The COMMUNIST Party has two concepts,The queueandWith \ asynchronous
  • Queues, understood as channels of execution, are divided intoSerial queuesandConcurrent queue, that is,sequentialandAt the same time to performDetermines the order of execution
  • Synchronous has no ability to start a new thread. Asynchronous has the ability to start a new thread

Serial queue, synchronous execution

  • Serial queue, sequential execution; Execute synchronously without opening new threads
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL); for (int i = 0; i < 10; I ++) {dispatch_sync(queue, ^{NSLog(@" serial queue, synchronous execution %@----- %d", [NSThread currentThread], I); }); }Copy the code

Serial queue, asynchronous execution

  • Serial queue, sequential execution; Asynchronously, new threads are opened, only one thread is opened
  • If the main thread tasks are executed at the same time, they are executed at the same time
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL); for (int i = 0; i < 10; I ++) {dispatch_async(queue, ^{NSLog(@" serial queue, async %@ ----- %d", [NSThread currentThread], I); }); }Copy the code

Concurrent queue, synchronous execution

  • Parallel queue, execute simultaneously; Synchronous execution, do not open new threads, resulting in sequential execution
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i < 10; I ++) {dispatch_sync(queue, ^{NSLog(@" parallel queue, synchronous execution %@ ----- %d", [NSThread currentThread], I); }); }Copy the code

Concurrent queue, asynchronous execution

  • Parallel queue, execute simultaneously; Asynchronous execution, open new threads, open multiple threads, disorderly execution
  • If the main thread is running at the same time, it will be executed at the same time. That is, if a new thread is started, it will be executed at the same time as the main thread
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i < 10; I ++) {dispatch_async(queue, ^{NSLog(@" parallel queue, async %@ ----- %d", [NSThread currentThread], I); }); }Copy the code

Main queue, asynchronous execution

  • The main queue is executed after the main thread tasks are completed.
  • Effect, on the main thread, executed sequentially
  • No new thread is started
for (int i = 0; i < 10; I ++) {dispatch_async(dispatch_get_main_queue(), ^{NSLog(@" primary queue, async %@ ----- %d", [NSThread currentThread], I); }); }Copy the code

Main queue and synchronous execution

  • New threads will not be started, and due to the characteristics of synchronous execution, tasks added to the main thread will wait for each other to complete, resulting in a crash and deadlock
  • The main queue is a serial queue, and tasks must be executed in sequence. After a synchronization task is added, the tasks in the main queue must wait until the synchronization task is completed. However, the program must wait for the synchronization task to be executed when it is executed downwards
for (int i = 0; i < 10; i++) {
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"同步执行 %@ ----- %d", [NSThread currentThread], i);

    });
}
Copy the code
  • Resolve the deadlock and execute the main thread task on the child thread
  • It waits for tasks on the main thread to complete
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ NSLog(@"%@", [NSThread currentThread]); for (int i = 0; i < 10; I ++) {dispatch_sync(dispatch_get_main_queue(), ^{NSLog(@" on child thread, execute primary queue %@ ----- %d", [NSThread currentThread], I); }); }});Copy the code

Global queue

  • Essentially a concurrent queue with no name
  • In practice, this is equivalent to manually setting up a concurrent queue
dispatch_get_global_queue(<#long identifier#>, <#unsigned long flags#>) /** quality of service * -qos_class_user_interactive * -qos_class_user_initiated * - QOS_CLASS_DEFAULT * -qOS_class_utility * -qOS_class_BACKGROUND * -dispatch_queue_priority_high: QOS_CLASS_USER_INITIATED * - DISPATCH_QUEUE_PRIORITY_DEFAULT: 0 QOS_CLASS_DEFAULT * - DISPATCH_QUEUE_PRIORITY_LOW: QOS_CLASS_UTILITY * - DISPATCH_QUEUE_PRIORITY_BACKGROUND: /** *@param flags *Reserved for future use. Passing any value other than zero may result in *a NULL return value. */Copy the code
  • Global queue simulation picture download
  • Back at the main thread, async and sync work the same way
for (int i = 0; i < 10; I ++) {dispatch_async(dispatch_get_global_queue(0, 0), ^{NSLog(@" download photo %@ ----- %d", [NSThread currentThread], I); [NSThread sleepForTimeInterval:3]; NSLog (@ download % @ "-- -- -- -- - % d", [NSThread currentThread], I); Dispatch_async (dispatch_get_main_queue(), ^{NSLog(@" refresh UI %@ ----- %d", [NSThread currentThread], I); }); }); }Copy the code

The Barrier blocking

  • Responsible for blocking multithreaded tasks that must be completedBarrierBefore the task is returned
  • The following effect is sequential execution on child threads
dispatch_queue_t queue = dispatch_queue_create("mzx", DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i < 10; I ++) {dispatch_barrier_async(queue, ^{NSLog(@" block %@ ----- %d", [NSThread currentThread], I); [NSThread sleepForTimeInterval:3]; }); }}Copy the code

After delayed execution

  • It’s a delay, but the timing is not perfect
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ for (int i = 0; i < 10; i++) { dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"after %@ ----- %d", [NSThread currentThread], i); }); }});Copy the code

Execute once

  • The principle determines that the value of a static global variable onceToken must be initialized to 0. The value is -1 after being executed once
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    NSLog(@"%@", [NSThread currentThread]);
});
Copy the code
  • To create a singleton
+ (instancetype)shared { static id instance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ if (instance == nil) { instance = [self new]; }}); return instance; }Copy the code

apply

  • Multithreaded loop execution
  • In the concurrent queue, directly execute the task at the same time; They’re in a serial queue, they’re executed sequentially; When placed in the main queue, it crashes, and the cause is executed simultaneously with the main queue
  • Concurrent queue
NSLog(@"apply---begin");
dispatch_apply(6, dispatch_get_global_queue(0, 0), ^(size_t index) {
    [NSThread sleepForTimeInterval:3];
    NSLog(@"%zd---%@",index, [NSThread currentThread]);
});
NSLog(@"apply---end");
Copy the code
  • Serial queues
NSLog(@"apply---begin");
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);
dispatch_apply(6, queue, ^(size_t index) {
        [NSThread sleepForTimeInterval:3];
        NSLog(@"%zd---%@",index, [NSThread currentThread]);
});
NSLog(@"apply---end");
Copy the code

Scheduling group

  • Objective to manage multiple asynchronous tasks
  • Multiple asynchronously executed codes are added to the group. After the tasks in the group are complete, the tasks in notify are executed
NSLog(@"currentThread---%@",[NSThread currentThread]); // Prints the current thread NSLog(@"group-- --begin"); dispatch_group_t group = dispatch_group_create(); Dispatch_group_async (dispatch_get_global_queue(0, 0), ^{NSThread sleepForTimeInterval:2]; NSLog(@" 1-- %@",[NSThread currentThread]); }); Dispatch_group_async (dispatch_get_global_queue(0, 0), ^{NSThread sleepForTimeInterval:2]; NSLog(@" download song 2-- %@",[NSThread currentThread]); }); ^{dispatch_group_notify(dispatch_get_main_queue(), dispatch_get_main_queue(), ^{ NSLog(@" download complete --%@",[NSThread currentThread]); NSLog(@"group---end"); });Copy the code
  • Blocking via WAIT
NSLog(@"currentThread---%@",[NSThread currentThread]); // Prints the current thread NSLog(@"group-- --begin"); dispatch_group_t group = dispatch_group_create(); Dispatch_group_async (dispatch_get_global_queue(0, 0), ^{NSThread sleepForTimeInterval:2]; NSLog(@" 1-- %@",[NSThread currentThread]); }); Dispatch_group_async (dispatch_get_global_queue(0, 0), ^{// Add task 2 [NSThread sleepForTimeInterval:10]; NSLog(@" download song 2-- %@",[NSThread currentThread]); }); dispatch_group_wait(group, DISPATCH_TIME_FOREVER); // The second parameter is DISPATCH_TIME_FOREVER, which effectively blocks NSLog(@"group-- end");Copy the code
  • Use leave and Enter to implement the group principle
  • By controlling whether the number of leave and Enter of the group is equal, the scheduling group is completed and then executednotfiyOr openwaitblocking
dispatch_group_t group = dispatch_group_create(); dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT); dispatch_group_enter(group); dispatch_async(queue, ^{ NSLog(@"task1"); NSLog(@"%@", [NSThread currentThread]); dispatch_group_leave(group); }); dispatch_group_enter(group); dispatch_async(queue, ^{ NSLog(@"task2"); NSLog(@"%@", [NSThread currentThread]); dispatch_group_leave(group); }); dispatch_group_enter(group); dispatch_async(queue, ^{ NSLog(@"task3"); NSLog(@"%@", [NSThread currentThread]); dispatch_group_leave(group); }); // dispatch_group_notify(group, dispatch_get_main_queue(), ^{// NSLog(@"over"); / /}); Dispatch_group_wait (group, DISPATCH_TIME_FOREVER); NSLog(@"hello");Copy the code

A semaphore

  • Dispatch_semaphore, count signal, use count to accomplish this function, count less than 0 wait, cannot pass. If the count is 0 or greater than 0, the count is subtracted by 1 and does not wait
  • The premise for using semaphores is to figure out which thread you need to handle waiting (blocking) and which thread needs to continue executing, and then use the semaphore
dispatch_semaphore_t semphore = dispatch_semaphore_create(0); // create a signal dispatch_semaphore_signal(semphore); // semaphore_wait(semphore, DISPATCH_TIME_FOREVER); // Semaphore -1Copy the code
  • Threads are synchronized, and asynchronous tasks are converted to synchronous execution
  • The semaphore will first decrease by 1 to -1, blocking; After the asynchronous task completes, the semaphore +1, signal 0, executes the following code
NSLog(@"currentThread---%@",[NSThread currentThread]); NSLog(@"semaphore---begin"); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // dispatch_semaphoRE_t semaphore = dispatch_semaphore_create(0); __block int number = 0; __block int number = 0; Dispatch_async (queue, ^{NSThread sleepForTimeInterval:2]) dispatch_async(queue, ^{NSThread sleepForTimeInterval:2]; NSLog(@" asynchronous execution --%@",[NSThread currentThread]); // Print the current thread number = 100; // Dispatch_semaphore_signal (semaphore); }); // 1 dispatch_semaphore_wait(DISPATCH_TIME_FOREVER); NSLog(@"semaphore---end,number = %d",number);Copy the code

NSOpreation

  • Based on GCD and object oriented
  • Is an abstract class, an unimplemented class, used as a parent class, constrained by subclasses that all have properties and methods in common

The difference with the COMMUNIST Party of China

  • GCD adds tasks to a queue (serial/concurrent/global/master) and executes them synchronously/asynchronously
  • NSOpreation adds asynchronous operations to a concurrent queue. You can set the maximum number of concurrent operations, suspend and continue the queue, cancel all operations, and establish dependencies between operations

NSInvocationOperation

NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invOPdemo) object:nil]; NSOperationQueue *queue = [NSOperationQueue new]; // Start a new thread and call the demo method [queue addOperation:op]; // call demo on main thread // [op start];Copy the code
  • The operation is executed after completion, on the current OP thread
[op setCompletionBlock:^{
    NSLog(@"%@", [NSThread currentThread]);
}];
Copy the code
  • Back to the main thread
[[NSOperationQueue mainQueue] addOperationWithBlock:^{NSLog(@" update UI %@", [NSThread currentThread]);}];Copy the code

NSBlockOperation

  • The effect is to call the code in the block directly
NSBlockOperation *bp = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"%@", [NSThread currentThread]);
}];
NSOperationQueue *queue = [NSOperationQueue new];
//    [queue addOperation:bp];
[bp start];
Copy the code
  • As long as the encapsulated operation is greater than 1, the concurrent queue is executed asynchronously
NSBlockOperation *bp = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"%@", [NSThread currentThread]); }]; // Add additional operation [bp addExecutionBlock:^{NSLog(@" 1.5---- %@", [NSThread currentThread]);}]; NSOperationQueue *queue = [NSOperationQueue new]; [queue addOperation:bp]; [queue addOperationWithBlock:^{ NSLog(@"%@", [NSThread currentThread]); }];Copy the code

Maximum concurrency

  • Simultaneous tasks
queue.maxConcurrentOperationCount = 3;
Copy the code

Rely on

NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{[NSThread sleepForTimeInterval:2]; NSLog(@" download %@", [NSThread currentThread]); }]; NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{[NSThread sleepForTimeInterval:1]; NSLog(@" unzip %@", [NSThread currentThread]); }]; NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@" upgrade done %@", [NSThread currentThread]);}]; [op2 addDependency:op1]; [op3 addDependency:op2]; NSOperationQueue *queue = [NSOperationQueue new]; [Queue addOperations:@[op1, op2] waitUntilFinished: yes] [queue addOperations:@[op1, op2] waitUntilFinished: yes] [[NSOperationQueue mainQueue] addOperation:op3];Copy the code

Service priority

qualityOfService
/**
 NSQualityOfServiceUserInteractive = 0x21,
 NSQualityOfServiceUserInitiated = 0x19,
 NSQualityOfServiceUtility = 0x11,
 NSQualityOfServiceBackground = 0x09,
 NSQualityOfServiceDefault = -1
 */
Copy the code
NSBlockOperation *bo1 = [NSBlockOperation blockOperationWithBlock:^{ for (int i = 0; i < 5; i++) { //sleep(1); NSLog (@ "the first operation % d - % @", I, [NSThread currentThread]);}}]; / / set the highest priority bo1. QualityOfService = NSQualityOfServiceUserInitiated; NSBlockOperation *bo2 = [NSBlockOperation blockOperationWithBlock:^{for (int I = 0; I < 5; I ++) {NSLog(@" second operation %d -- -- %@", i, [NSThread currentThread]); } }]; / / set the lowest priority bo2. QualityOfService = NSQualityOfServiceUserInteractive; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperation:bo1]; [queue addOperation:bo2];Copy the code

Pause, continue, cancel

  • And you can see here,NSOperationQueueIs to add all tasks to a queue and execute them simultaneously based on the maximum number of concurrent tasks
  • Cancel the current task and cancel subsequent operations
- (IBAction)cancel:(id)sender {// the current operation will finish, cancel subsequent operations [self.queue cancelAllOperations]; / / output the number of tasks remaining NSLog (@ "cancel - % lh-zd", the self. The queue. The operationCount); } - (IBAction)suspend:(id)sender {// the current operation is completed, the subsequent operation is suspended self.queue.suspended = YES; NSLog(@"suspend"); } - (IBAction)continue:(id)sender { self.queue.suspended = NO; NSLog(@"continue"); }Copy the code

The custom Operation

  • Each callnewMethod will be calledmain
  • rewritemainMethod,
- (void)main{@autoreleasepool {// Assert the reason for sending. = nil, @"finishBlock cannot be nil"); [NSThread sleepForTimeInterval:2]; If (self.isCancelled) {return; // check whether the operation isCancelled. } // simulate the download of NSLog(@" download image %@ %@", [NSThread currentThread], self.urlString); // If (self.finishedBlock) {// Back to the main thread update UI [[NSOperationQueue mainQueue] addOperationWithBlock:^{ self.finishedBlock(self.urlString); }]; / /}}}Copy the code
  • Custom operation functions
+ (instancetype)downloadOperationWithURLString:(NSString *)urlString andFinishedBlock:(void (^)(NSString *))finishedBlock
{
    MYDownloadOperation *op = [SSDownloadOperation new];
    op.urlString = urlString;
    op.finishedBlock = finishedBlock;
    return op;
}
Copy the code
  • With effects, you can cancel all operations without completing the current task
for (int i = 0; i < 20; i++) { SSDownloadOperation *op = [SSDownloadOperation downloadOperationWithURLString:@"abc.jpg" NSLog(@" update UI %@ %@ %d", img, [NSThread currentThread], I);}]; [self.queue addOperation:op]; }Copy the code