The last article introduced the basic use of THE GCD

1.NSThread Each NSThread object corresponds to a thread, which is the original thread. 1) Advantages: NSThread is lightweight and relatively simple. 2) Disadvantages: Manually manage all thread activities such as life cycle, thread synchronization, sleep, etc.

2.NSOperation has its own abstract class for thread management. 1) Advantages: With thread cycle management, you can pay more attention to your own logic in operation. 2) Disadvantages: Object-oriented abstract class, can only implement it or use its definition of two subclasses: NSInvocationOperation and NSBlockOperation.

3.GCD Grand Central Dispatch (GCD) is a multi-core programming solution developed by Apple. 1) Advantages: Most efficient and avoids concurrency traps. 2) Disadvantages: Based on C implementation.

4. Select summary 1) simple and safe selection of NSOperation to achieve multithreading. 2) Choose GCD to handle large amount of concurrent data and pursue performance and efficiency. 3) Nsthreads are generally not recommended in programs that frequently use multithreading

0x00 – NSThread

NSThread is an object-oriented thread operation technology officially provided by Apple. It is the upper encapsulation of thread and is more low-level. It is simple and convenient and can directly operate objects, but it is seldom used. You often use [NSThread currentThread] to get the currentThread

Official reference NSThread

IOS advanced multithreading -NSThread details

Create a thread

  • throughalloc initInitial creation
  • throughdetachNewThreadSelectorConstructor mode
  • Implicit creation passesperformSelector...Method created primarily for use inThe main threadandA background threadPerform a task
  • Custom inheritanceNSThreadClass, rewritemainMethods, you don’t have to write in methodssuper
@interface MyThread: NSThread @end @implementation MyThread - (void)main { NSLog(@"%s --- %@", __func__, [NSThread currentThread]); } @end //1, create - (void)test_createNSThread{NSString *threadName1 = @"NSThread1"; NSString *threadName2 = @"NSThread2"; NSString *threadName3 = @"NSThread3"; NSString *threadNameMain = @"NSThreadMain"; // Method 1: Initialization mode, You need to manually enable NSThread *threadWithArgs = [[NSThread alloc] initWithTarget:self selector:@selector(doSomething:) object:threadName1]; NSThread *thread1 = [[NSThread alloc] init]; NSThread *threadWithBlock = [[NSThread alloc] initWithBlock:^{ NSLog(@"threadWithBlock - %@", [NSThread currentThread]); }]; thread1.threadPriority = 1; // Set the priority of the thread (0.0-1.0, 1.0 superlative) [threadWithArgs start]; MyThread *myThread = [[MyThread alloc] init]; [myThread start]; [NSThread detachNewThreadSelector:@selector(doSomething:) toTarget:self withObject:threadName2]; [NSThread detachNewThreadWithBlock:^{ }]; // Select selector... Method to create [self performSelectorInBackground: @ the selector (doSomething:) withObject: threadName3]; / / way four [self performSelectorOnMainThread: @ the selector (doSomething:) withObject: threadNameMain waitUntilDone: YES]; } - (void)doSomething:(NSObject *)objc{ NSLog(@"%@ - %@", objc, [NSThread currentThread]); }Copy the code

Commonly used attributes

IsCancelled // whether the thread isCancelled. - thread.isFinished // whether the thread is complete. - thread.isMainThread // whether the thread is the main thread - thread.threadPriority // Priority of a thread. The value ranges from 0.0 to 1.0. The default priority is 0.5Copy the code

Common class methods

@property (class, readonly, strong) NSThread *currentThread; // get the current thread + (void)sleepUntilDate:(NSDate *)date; + (void)sleepForTimeInterval:(NSTimeInterval)ti; // How long the current thread sleeps + (void)exit; @property(class, readonly, strong) NSThread *mainThread; // Get the main threadCopy the code
- (void)test_NSThreadClassMethod{// currentThread [NSThread currentThread]; // If number=1, NSLog(@"%@", [NSThread currentThread]); [NSThread sleepForTimeInterval:2]; [NSThread sleepUntilDate:[NSDate date]]; // other [NSThread exit]; // Exit thread [NSThread isMainThread]; // Check whether the threaded thread is the active thread [NSThread isMultiThreaded]; NSThread mainThread = [NSThread mainThread]; // mainThread object NSLog(@"%@", mainThread);Copy the code

0x01 – GCDThe advanced

dispatch_once

/* Dispatch_once Ensures that codes in the block are executed only once during App execution. Application scenarios: singleton, method-swizzling */ static dispatch_once_t onceToken; Dispatch_once (&onceToken, ^{// create singleton, method swizzled or other task NSLog(@" create singleton "); });Copy the code

dispatch_after

/* Dispatch_after Delay execution of blocks in a queue Application scenario: Delay the execution of a task on the main queue, such as one second after viewDidload, prompting an alertView. */ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{NSLog(@"2s after output "); });Copy the code

dispatch_apply

/* dispatch_apply applies the specified Block to the specified queue and waits until all processing has finished. It is used to calculate the size of each control in advance after pulling network data to prevent calculation during drawing. Improved form sliding fluency - Add to serial queue - Sequential execution - Add to primary queue - deadlock - Add to concurrent queue - Out-of-order execution - Add to global queue - out-of-order execution */ dispatch_queue_t queue = dispatch_queue_create("CJL", DISPATCH_QUEUE_SERIAL); NSLog (@ "before dispatch_apply"); /** param1: indicates the number of repetitions param2: indicates the queue to be appended param3: */ dispatch_apply(10, queue, ^(size_t index) {NSLog(@"dispatch_apply thread %zu - %@", index, [NSThread currentThread]); }); NSLog (@ "after dispatch_apply");Copy the code

dispatch_groupTwo ways to cooperate with the use

A queue group can add many queues to a group. The advantage of this is that the queue group has a method to notify us when all the tasks in the group have finished.

Dispatch_group_async monitors the completion of a group of tasks and is notified to perform other operations. This method is useful, for example, if you perform three downloads, you will only notify the interface that it is complete when all three downloads are complete.

  • usedispatch_group_async + dispatch_group_notify
/* dispatch_group_t: dispatch_group_t: dispatch_group_t: dispatch_group_t: dispatch_group_t: dispatch_group_t: dispatch_group_t: dispatch_group_t: dispatch_group_t: dispatch_group_create(); dispatch_queue_t queue = dispatch_get_global_queue(0, 0); Dispatch_group_async (group, queue, ^{NSLog(@" request done "); }); Dispatch_group_async (group, queue, ^{NSLog(@" request two done "); }); Dispatch_group_notify (dispatch_get_main_queue(), ^{NSLog(@" refresh page "); });Copy the code
  • usedispatch_group_enter + dispatch_group_leave + dispatch_group_notify
*/ dispatch_group_t group = dispatch_group_create(); /* dispatch_group_leave = dispatch_group_create(); dispatch_queue_t queue = dispatch_get_global_queue(0, 0); dispatch_group_enter(group); Dispatch_async (queue, ^{NSLog(@" request done "); dispatch_group_leave(group); }); dispatch_group_enter(group); Dispatch_async (queue, ^{NSLog(@" request two done "); dispatch_group_leave(group); }); Dispatch_group_notify (dispatch_get_main_queue(), ^{NSLog(@" refresh interface "); });Copy the code
  • increasedispatch_group_waitfunction
/* long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout) group: Timeout to wait (how long to wait) - DISPATCH_TIME_NOW means that the dispatch group is not waiting for completion - DISPATCH_TIME_FOREVER blocks the current dispatch group until completion: */ dispatch_group_t group = dispatch_group_create(); */ dispatch_group_t group = dispatch_group_create(); dispatch_queue_t queue = dispatch_get_global_queue(0, 0); dispatch_group_enter(group); Dispatch_async (queue, ^{NSLog(@" request done "); dispatch_group_leave(group); }); dispatch_group_enter(group); Dispatch_async (queue, ^{NSLog(@" request two done "); dispatch_group_leave(group); }); // long timeout = dispatch_group_wait(group, DISPATCH_TIME_NOW); // long timeout = dispatch_group_wait(group, DISPATCH_TIME_FOREVER); long timeout = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 1 *NSEC_PER_SEC)); NSLog(@"timeout = %ld", timeout); If (timeout == 0) {NSLog(@" finish task on time "); }else{NSLog(@" timeout "); } dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@" refresh interface "); });Copy the code

dispatch_barrier_sync & dispatch_barrier_asyncBarrier function

Dispatch_barrier_async is executed after the previous task has finished, and subsequent tasks are executed after it has completed.

  • Serial queues
- (void)testBarrier{/* dispatch_barrier_sync & dispatch_barrier_async Application scenario: After the tasks appended to the queue, such as synchronization lock, are completed, the tasks appended to the queue behind the fence are appended to the queue. - dispatch_barrier_async: dispatch_barrier_sync: dispatch_barrier_sync: - dispatch_barrier_async can control the order in which tasks are executed in the queue. - Dispatch_barrier_sync not only blocks the queue, Also blocks thread execution (minimize use) */ [self testBarrier1]; [self testBarrier2]; } - (void)testBarrier1{dispatch_queue_t queue = dispatch_queue_create("CJL", DISPATCH_QUEUE_SERIAL); NSLog(@" start - %@", [NSThread currentThread]); dispatch_async(queue, ^{ sleep(2); NSLog(@" delay task for 2s 1 - %@", [NSThread currentThread]); }); NSLog(@" first end - %@", [NSThread currentThread]); // The barrier function groups tasks in a queue. So we just focus on task 1 and task 2 dispatch_barrier_async (queue, ^ {NSLog (@ "-- -- -- -- -- -- -- -- -- -- -- -- the fence task -- -- -- -- -- -- -- -- -- -- - % @", [NSThread currentThread]); }); NSLog(@" end of fence - %@", [NSThread currentThread]); dispatch_async(queue, ^{ sleep(2); NSLog(@" delay task for 2s 2 - %@", [NSThread currentThread]); }); NSLog(@" end second - %@", [NSThread currentThread]); } - (void)testBarrier2{dispatch_queue_t queue = dispatch_queue_create("CJL", DISPATCH_QUEUE_CONCURRENT); NSLog(@" start - %@", [NSThread currentThread]); dispatch_async(queue, ^{ sleep(2); NSLog(@" delay task for 2s 1 - %@", [NSThread currentThread]); }); NSLog(@" first end - %@", [NSThread currentThread]); // Because the tasks in the concurrent queue are executed out of order, So use the fence function can be very good control over the order of tasks within the queue dispatch_barrier_async (queue, ^ {NSLog (@ "-- -- -- -- -- -- -- -- -- -- -- -- the fence task -- -- -- -- -- -- -- -- -- -- -- -- % @". [NSThread currentThread]); }); NSLog(@" end of fence - %@", [NSThread currentThread]); dispatch_async(queue, ^{ sleep(2); NSLog(@" delay task for 2s 2 - %@", [NSThread currentThread]); }); NSLog(@" end second - %@", [NSThread currentThread]); }Copy the code

dispatch_semaphore_tA semaphore

Semaphores are primarily used as synchronization locks to control the maximum number of concurrent GCDS

-dispatch_semaphore_create () : creates a semaphore. -dispatch_semaphore_wait () : waits for a semaphore. If the semaphore is < 0, the current thread is blocked based on the incoming wait time. If the wait is permanent, the next action will wait for signal. - dispatch_semaphore_signal() : the semaphore is released and the semaphore is incremented by 1. */ dispatch_queue_t queue = dispatch_queue_create("CJL", DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i < 10; I ++) {dispatch_async(queue, ^{NSLog(@" current - %d, thread - %@", I, [NSThread currentThread]); }); } // dispatch_semaphore_t sem = dispatch_semaphore_create(0); for (int i = 0; i < 10; I ++) {dispatch_async(queue, ^{NSLog(@" current - %d, thread - %@", I, [NSThread currentThread]); dispatch_semaphore_signal(sem); }); dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); }Copy the code

dispatch_source_t

Dispatch_source_t is primarily used for timing operations because it creates timers that do not rely on RunLoop and are more accurate than NSTimer

/* Dispatch_source application scenario: GCDTimer typically uses NSTimer for timing logic in iOS development, but NSTimer relies on Runloop, which can run in different modes. If NSTimer is added in one mode, the timer hangs up while Runloop is running in another mode; If Runloop is blocked, NSTimer firing is delayed until the next Runloop cycle. The dispatch_source is a basic data type that can be used to listen for some low-level system events. Timer event Source, used to generate periodic notifications or callbacks. -signal Dispatch Source: Listen for Signal event Source, which can be used when UNIX signals occur. -Mach port Dispatch Source -Mach port Dispatch Source -Mach port Dispatch Source: -custom Dispatch Source: -dispatch_source_create: -dispatch_source_create: -dispatch_source_set_event_handler: Sets the data source callback. -dispatch_source_merge_data: -dispatch_source_get_data: obtain the event source data. -dispatch_resume: continue. -dispatch_suspend: suspend. -dispatch_cancle: cancel. Dispatch_queue_t queue = dispatch_get_global_queue(0, 0); //2. Create timer dispatch_source_t timer = dispatch_source_create(dispatch_source_timer, 0, 0, queue); Dispatch_source_set_timer (timer, DISPATCH_TIME_NOW, 2.0*NSEC_PER_SEC, 0.1*NSEC_PER_SEC); Dispatch_source_set_event_handler (timer, ^{NSLog(@"GCDTimer")); }); //5. By default, dispatch_resume(timer) needs to be activated.Copy the code

0x02 – NSOperation

NSOperation is apple’s encapsulation of GCD and is fully object-oriented. NSOperation and NSOperationQueue correspond to GCD tasks and queues respectively. So NSOperation needs to work with NSOperationQueue to implement multithreading.

The steps are also easy to understand:

  1. The task to be performed is encapsulated in an NSOperation object.
  2. Add this task to an NSOperationQueue object.
  3. Add the task to the queueNSOperation objectAdded to theNSOperationQueue
  1. Use a subclassNSInvocationOperation
NSInvocationOperation * Operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil]; //2. Start [operation start];Copy the code
  1. Use a subclassNSBlockOperation

In addition to the two operations above, we can also customize operations. To customize Operation, you need to inherit the NSOperation class and implement its main() method, because the main() method is called internally to complete the logic when the start() method is called. So if the above two classes do not satisfy your desire, you need to customize. Whatever function you want to implement can be written in it. In addition, you need to implement various methods including cancel().

If it is to be executed concurrently, it must be overridden

  • start
  • asynchronous
  • executing
  • finished
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"%@", [NSThread currentThread]); }]; //2. Start task [operation start];Copy the code

NSOperationQueue

NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSOperationQueue *mainQueue = [NSOperationQueue mainQueue]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; // Add a NSOperation [queue addOperation:operation] // Add a group of nsOperations [queue addOperations:operations WaitUntilFinished :NO // Add a block Operation [queue addOperationWithBlock:^{// Perform a block Operation}]; [queue SetMaxConcurrentOperationCount: 1); / / a single NSOperation cancel [operation cancel] / / cancel all operations of NSOperationQueue [queue CancelAllOperations] // Suspend queue [queue setSuspended:YES];Copy the code

NSOperationQueue has two different types of queues: primary queue and custom queue (tasks added to ‘non-queue’ are concurrent by default, with multi-threading enabled). . The main queue runs on top of the main thread, while custom queues execute in the background. In both types, the tasks handled by these queues are expressed using subclasses of NSOperation.

/* NSInvocationOperation and NSBlockOperation are different: - the former is similar to target - the latter is similar to block - functional programming, business logic code is more readable NSOperationQueue is executed asynchronously, NSBlockOperation *bo = [NSBlockOperation blockOperationWithBlock:^{ NSLog (@ "task 1 -- - % @", [NSThread currentThread]);}]; // Add transaction [bo addExecutionBlock:^{NSLog(@" task 2————%@",[NSThread currentThread]);}]; Bo.pletionblock = ^{NSLog(@" done!!") ); }; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperation:bo]; NSLog(@" transaction added to NSOperationQueue");Copy the code
  • Set sequential output
NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue setMaxConcurrentOperationCount:1]; // Controls the number of concurrent tasks. When set to 1, it is a serial queue. The primary column defaults to a serial queue for (int I = 0; i < 5; i++) { [queue addOperationWithBlock:^{ NSLog(@"%@---%d", [NSThread currentThread], i); }]; }Copy the code
  • Setting the Priority
/* NSOperation setting the priority will only make the CPU more likely to call, It is not necessary to set high to complete all tasks first - do not use sleep - high priority task 1 precedes low priority task 2 - use sleep for delay - high priority task 1 is slower than low priority task 2 */ NSBlockOperation *bo1 = [NSBlockOperation blockOperationWithBlock:^{for (int I = 0; I < 5; I ++) {//sleep(1); NSLog(@" first operation %d -- %@", I, [NSThread currentThread]); } }]; / / set the highest priority bo1. QualityOfService = NSQualityOfServiceUserInteractive; NSBlockOperation *bo2 = [NSBlockOperation blockOperationWithBlock:^{for (int I = 0; I < 5; I ++) {NSLog(@" second operation %d -- -- %@", i, [NSThread currentThread]); } }]; / / set the lowest priority bo2. QualityOfService = NSQualityOfServiceBackground; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperation:bo1]; [queue addOperation:bo2];Copy the code
  • Setting the number of concurrent requests
/ * in the GCD can only use semaphore to set the concurrency and can set the number of concurrent NSOperation easily by setting the maxConcurrentOperationCount to control a single queue to perform task queue number * / NSOperationQueue * = [[NSOperationQueue alloc] init]; queue.name = @"Felix"; queue.maxConcurrentOperationCount = 2; for (int i = 0; i < 5; I ++) {[queue addOperationWithBlock:^{// a task [NSThread sleepForTimeInterval:2]; NSLog(@"%d-%@", I,[NSThread currentThread]); }]; }Copy the code
  • Add the dependent

NSOperation has a very useful function, which is to add dependencies. For example, there are three tasks: A: download an image from the server, B: add A watermark to the image, and C: return the image to the server. This is where dependencies come in:

//1. Task 1: NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@" download picture - %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:1.0]; //2. Task 2: NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@" watermark - %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:1.0]; //3. Task 3: NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@" upload image - %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:1.0]; //4. Set dependency [operation2 addDependency:operation1]; // Task two depends on task one [operation3 addDependency:operation2]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];Copy the code
  • Thread communication
NSOperationQueue *queue = [[NSOperationQueue alloc] init]; queue.name = @"Felix"; [queue addOperationWithBlock:^{NSLog(@" request network %@--%@", [NSOperationQueue currentQueue], [NSThread currentThread]); [[NSOperationQueue mainQueue] addOperationWithBlock:^{NSLog(@" refresh UI%@--%@", [NSOperationQueue currentQueue], [NSThread currentThread]); }]; }];Copy the code

IOS Multithreading summary

Welcome big brother message correction 😄, code word is not easy, feel good to give a thumbs-up 👍 have any expression or understanding error please leave a message; Common progress;