Introduction: In iOS, there are four multithreaded scenarios: Pthread, Nsthread, Nsoperation & Nsoperationqueue, and GCD, but GCD is used most in development, This article mainly summarizes the situation that I use GCD.
I. Overview of GCD (Grand Central Dispatch)
1. Basic Concepts
GCD allows a program to split a task into multiple single tasks , commit to the dispatch Queue, and then dispatch the thread for concurrent or serial execution of the task. GCD hides the scheduling of internal threads, developers only need to focus on creating or acquiring queues, and then appending blocks to the queue.
There are two types of queues in iOS, serial queues and concurrent queues , respectively
Serial Queue : Only one task is executed in the same time queue, and each task can begin execution only after the previous task has completed execution. Home Row (Obtained through Dispatch_get_main_queue (), the task submitted to the home row is executed in the main thread) is the serial queue, or you can create a serial queue using Dispatch_queue_create.
Serial queue. png
- concurrent Queues : These tasks start executing in the order in which they are added. However, there are multiple blocks (tasks) running at any one time, which is entirely dependent on GCD. Concurrent queues can be created using dispatch_queue_create, or you can get global queues in the process, with the global queue: High, Medium (default), and three priority queues low. You can call the Dispatch_get_global_queue function to pass in the appropriate priority to access the queue.
Concurrent queues. png
synchronous Execution : Blocks the current thread until the task is completed in the current block and returns. Synchronization does not create a new thread. You cannot use sync to add tasks to the main queue, which can cause deadlocks.
Asynchronous Execution : The current thread is not blocked, the function returns immediately, the block executes asynchronously in the background, and the new thread is bound to open asynchronously.
Illustration 1: It is not right for some blogs to say concurrent queues as parallel queues . Because parallelism occurs at the same time as multiple events, concurrency is occurring at the same interval, and parallelism depends entirely on the processor's number of cores. concurrently , each core of the processor can be fully utilized to achieve the highest processing performance.
Description 2: The queue is not equal to a thread. As a developer, we just add blocks to the appropriate GCD queue, and the actual thread is scheduled to be completed by the system, regardless of whether synchronization (sync) or async (async) commits the block to the host column , and eventually the block is executed in the main thread Synchronization (Sync) commits a block to a non-home column , executes in the current thread, and executes in a sub-thread if asynchronous (async) commits the block to a non-home column .
2, serial queue/concurrent queue and synchronous/asynchronous combination (focus)
There are four ways of combination
1) Serial queue + synchronous combination (common)
dispatch_queue_t serialQueue = dispatch_queue_create("com.serial.queue", DISPATCH_QUEUE_SERIAL);// dispatch_queue_t serialQueue = dispatch_queue_create("com.serial.queue", NULL);//dispatch_sync(serialQueue, ^{ NSLog(@"串行队列 + 同步:%@",[NSThread currentThread]);});dispatch_sync(serialQueue, ^{ NSLog(@"串行队列 + 同步:%@",[NSThread currentThread]);});dispatch_sync(serialQueue, ^{ NSLog(@"串行队列 + 同步:%@",[NSThread currentThread]);});
Serial queue + synchronous combined result. png
Illustration 1: the serial queue (the serial thread that you created) + synchronization combination, does not create a new thread and still executes the task on the current thread. It is not possible to use the Sync method in the main thread, which can cause deadlocks.
NOTE 2: More commonly used, synchronous locking alternative methods.
2) Serial queue + asynchronous combination
dispatch_queue_t serialqueue = dispatch_queue_create ( " Com.serial.queue ", dispatch_queue_serial) ;//dispatch_queue_t serialqueue = Dispatch_ Queue_create ( "Com.serial.queue", NULL) ;//dispatch_async ( Span class= "Hljs-name" >serialqueue, ^{NSLog (@ "Serial queue + Async:%@", [Nsthread CurrentThread]) Span class= "Hljs-comment";}) ;d Ispatch_async (serialqueue, ^{NSLog (@ "Serial queue + Async:%@", [Nsthread CurrentThread]) ;d Ispatch_async (serialqueue, ^{NSLog (@ "Serial queue + Async:%@", [Nsthread CurrentThread])
Serial queue + asynchronous combined result. png
Description : the serial queue (either by itself or the home row) + asynchronous combination creates a new thread, but only one thread is opened;
3) Concurrent queue + Sync combination
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);dispatch_sync(concurrentQueue, ^{ NSLog(@"并发队列 + 同步1:%@",[NSThread currentThread]);});dispatch_sync(concurrentQueue, ^{ NSLog(@"并发队列 + 同步2:%@",[NSThread currentThread]);});dispatch_sync(concurrentQueue, ^{ NSLog(@"并发队列 + 同步3:%@",[NSThread currentThread]);});
Concurrent Queue + synchronous combined result. png
Description : The concurrent queue (either by itself or by the global queue) + synchronization combination, and no new thread is created, and the task is still executing on the current thread.
4) Concurrent Queue + asynchronous combination (common)
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);dispatch_async(concurrentQueue, ^{ NSLog(@"并发队列 + 异步:%@",[NSThread currentThread]);});dispatch_async(concurrentQueue, ^{ NSLog(@"并发队列 + 异步:%@",[NSThread currentThread]);});dispatch_async(concurrentQueue, ^{ NSLog(@"并发队列 + 异步:%@",[NSThread currentThread]);});
Concurrent Queue + asynchronous combined results. png
Description : The concurrent queue (either by itself or by the global queue) + asynchronous combination creates a new thread and can open multiple threads in the IOS system.
|
Synchronous |
Asynchronous |
Serial queue |
1, will not create a new thread, still perform the task on the current thread, 2, similar to the synchronous lock, is a synchronous lock alternative |
1, will create a new thread, but only one thread, 2, each time using Dispatch_queue_create to create a serial queue, a new thread will be created, multiple creation, will create multiple threads, multiple threads concurrent execution. |
Concurrent queues |
Does not create a new thread and still executes the task on the current thread |
1, will create a new thread, you can open more than one thread; 2, ios7-sdk era is generally 5, 6, IOS8-SDK can be 50, 60 |
Summary 1: You cannot use the Sync method in the main thread, or it will cause a deadlock.
Summary 2: Serial queue + synchronous combination can replace the synchronous lock;
Summary 3: In order to improve efficiency, such as multi-threaded download pictures, etc., concurrent queue + asynchronous more commonly used.
Second, GCD use 1: Asynchronous processing
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_async(globalQueue, ^{ // 一个异步的任务,如网络请求,耗时的文件操作等等 ... dispatch_async(dispatch_get_main_queue(), ^{ // UI刷新 或其他主线程操作 ... });});
Description : This usage is most common, such as opening an asynchronous network request, refreshing the UI on the main thread after the data is returned, and so on.
Third, GCD use 2: Single case
Dispatch_once implementation of a single case, in order to achieve the Qsaccountmanager single case for example. The source code is as follows:
1. Realize
Qsaccountmanager.m@implementationQsaccountmanagerstatic Qsaccountmanager *_sharemanager =nil;+ (Instancetype) sharemanager{Staticdispatch_once_t once; dispatch_once (&once, ^{_sharemanager = [[self alloc] INIT]; }); return _sharemanager;} + (instancetype) Allocwithzone: (struct _NSZone *) zone{static dispatch_once_t Oncetoken; dispatch_once (&oncetoken, ^{_sharemanager = [super Allocwithzone:zone]; }); return _sharemanager;} -(nonnull id) Copywithzone: ( Nullable nszone *) zone{return _sharemanager;} @end
Note 1: In the Dispatch_once function, parameter 1 is the predicate that the code block is called, and Parameter 2 is the code block that is called. The code blocks in the function are only executed once and are thread-safe.
Note 2: To ensure that the Singleton class has only one unique instance, you also need to implement the Allocwithzone and Copywithzone methods, which guarantees that using the Init and copy methods returns is also the only instance.
1. function
shareManager];QSAccountManager *account2 = [QSAccountManager new];QSAccountManager *account3 = [[QSAccountManager alloc]init];QSAccountManager *account4 = [account3 copy];NSLog(@"account1 = %@",account1);NSLog(@"account2 = %@",account2);NSLog(@"account3 = %@",account3);NSLog(@"account4 = %@",account4);
Single-instance output results. png
Iv. GCD use 3: Replace the sync lock
The memory management semantics of Atomic are atomic, guaranteeing only that the setter and getter methods of the attributes are atomic, but the execution is inefficient and can be implemented using GCD.
The @synchronized (self) synchronization block mechanism automatically creates a lock based on the given object, and waits for the code in the block to complete before releasing the lock. Inefficient execution.
Alternative : To put data read and write into the serial synchronization queue , to ensure data synchronization, thread safety.
Alternative : Data synchronization with GCD fence blocks (barrier) and concurrent queues for thread safety. (more efficient than serial synchronous queue mode)
1. Implement thread-safe setter and getter method instead of atomic
//串行队列_syncQueue = dispatch_queue_create("com.jzp.syncQueue",NULL);//假设属性是someString- (NSString *)someString { __block NSString *localSomeString; dispatch_sync(_syncQueue, ^{ localSomeString = _someString; }); return localSomeString;}- (void)setSomeString:(NSString *)someString { dispatch_sync(_syncQueue, ^{ _someString = someString; });}
2. Implement thread-safe nsmutablearray
Mainly rely on the characteristics of the fence block alone, in the concurrent queue if it is found that the next processing block is a fence block, then until all the current concurrent blocks are executed, the fence block will be executed alone. After the fence block is executed, proceed to the downward process as normal.
This part of the implementation is detailed in the iOS record 12:nsmutablearray used in the issue of "one, thread-safe nsmutablearray".
description : Dispatch_barrier_sync and Dispatch_barrier_async are only valid on concurrent queues created by themselves, and on global (global) concurrent queues, serial queues, with Dispatch_ (a) Sync effect is the same.
v. GCD using 4:dispatch_group for thread synchronization
1. Simple mode
dispatch_queue_tqueue = Dispatch_get_global_queue (Dispatch_queue_priority_default, 0);d ispatch_group_t group = Dispatch_group_create ();d Ispatch _group_async (group, queue, ^{/ /Task 1});d Ispatch_group_async (group, queue, ^{//Task 2}); //wait for multiple asynchronous tasks in the group to complete, will emit a synchronous signal group, dispatch_time_forever); //... //Mode 2 (does not block the current thread, the task is completed on group, execute block code) dispatch_group_notify ( group, Dispatch_get_main_queue (), ^{//after the task is completed, do some action in the main queue});
Description : The Block (Task) is placed in the queue and associated with the dispatch group, and if the block submitted to the dispatch queue is executed, the block code in the dispatch_group_notify is executed; or before the task is completed on the group, dispatch_group_wait blocks the current thread (so it cannot be placed on the main thread call) and waits until the task is completed on the group, or the wait time exceeds the set timeout period.
2. Synchronization of multiple asynchronous tasks
Paired with Dispatch_group_enter and dispatch_group_leave, asynchronous tasks can be added to the group;
When these asynchronous tasks are processed, dispatch_group_notify and Dispatch_group_wait will receive a synchronization signal;
Asynchronous tasks, such as requests, enable the processing of bulk requests through this mechanism.
dispatch_group_t Batch_request_group = dispatch_group_create ();d ispatch_group_enter (Batch_request_group); [Self.request1 startwithcompleteblock:^ (BOOL issuccess,ID _nullable responseobj,NSString * _nonnull Errordesc) {//todo data Parsing .... Dispatch_group_leave (Batch_request_group);}]; Dispatch_group_enter (Batch_request_group); [self.request2 startwithcompleteblock:^ (BOOL isSuccess, Span class= "Hljs-keyword" >id _nullable responseobj, nsstring * _nonnull errorDesc) {//todo data Parsing .... Dispatch_group_leave (Batch_request_group);}]; Dispatch_group_enter (Batch_request_group); [self.request3 startwithcompleteblock:^ (BOOL isSuccess, Span class= "Hljs-keyword" >id _nullable responseobj, nsstring * _nonnull errorDesc) {//todo data Parsing .... Dispatch_group_leave (Batch_request_group);}]; Dispatch_group_notify (Batch_request_group, Dispatch_get_main_queue (), ^{//three requests are over, Continue processing});
Vi. GCD use of other
1, dispatch_apply
Appends the specified block to the specified dispatch queue for a specified number of times, and waits until all processing execution finishes. There are parallel operating mechanisms that are generally more efficient than the for-loop class-serialization mechanism.
10 指定重复次数,这里指定10次 @param gQueue 追加对象的Dispatch Queue @param index 带有参数的Block, index的作用是为了按执行的顺序区分各个Block */dispatch_apply(10, gQueue, ^(size_t index) { NSLog(@"%zu",index);});
2, Dispatch_after
Deferred execution
// NSEC_PER_SEC,每秒有多少纳秒。// USEC_PER_SEC,每秒有多少毫秒。// NSEC_PER_USEC,每毫秒有多少纳秒。// DISPATCH_TIME_NOW 从现在开始// DISPATCH_TIME_FOREVE 永久// time为5sdispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW,(int64_t)(5.0 * NSEC_PER_SEC));dispatch_queue_t queue = dispatch_get_main_queue();dispatch_after(time, queue, ^{ // 在queue里面延迟执行的一段代码 // ...});
3. Beware of deadlocks
- Deadlock Scenario 1: using the Sync method in the main thread
dispatch_sync(dispatch_get_main_queue(), ^{ // 任务 ...});
- Deadlock Condition 2: add synchronization task in serial queue;
// 在串行队列添加同步任务 dispatch_sync(serialQueue, ^{ // 任务 dispatch_sync(serialQueue, ^{ // 任务 });};
End
Nanhua Coder
Links: http://www.jianshu.com/p/e1784f8172c0
Source: Pinterest
Copyright belongs to the author. Commercial reprint please contact the author for authorization, non-commercial reprint please specify the source.
iOS transcript: GCD usage Summary (i)