• IOS multithreading, spin locks and mutex details
  • NSThread for iOS multithreading
  • NSOperation for iOS multithreading
  • IOS Multithreading GCD

1 the GCD is briefly

Apple source – Dispatch

Grand Central Dispatch (GCD) is a relatively new solution to multi-core programming developed by Apple. It is primarily used to optimize applications to support multicore processors and other symmetric multiprocessing systems. It is a concurrent task executed on a thread pool basis. First released on Mac OS X 10.6 Snow Leopard and also available on iOS 4 and above.

The GCD advantages

  • GCDCan be used for multi-core parallel computing
  • GCDWill automatically utilize moreCPUCore (e.g. Dual-core, quad-core)
  • GCDAutomatically manages the thread lifecycle (thread creation, task scheduling, thread destruction)
  • The programmer just needs to tellGCDWhat do you want to do without writing any thread management code

2 GCD tasks and queues

task

Task: Execute an action, that is, the piece of code that you execute in a thread. In GCD it is placed in a block. A task can be executed synchronously or asynchronously

  • Synchronous execution (sync): (1) Add tasks to the specified queue synchronously. The system waits until the tasks in the queue are completed before continuing to execute tasks. (2) The system can execute tasks in the current thread and does not have the ability to start new threads
  • Asynchronous execution (async(1) Asynchronously add the task to the specified queue, it does not do any wait, can continue to execute the task (2) can execute the task in a new thread, with the ability to start a new thread

Note: Async has the ability to start a new thread, but it does not have to start a new thread. This depends on the type of queue specified by the task (described below)

Tasks can be created using dispatch_sync or dispatch_async

Dispatch_sync (queue, ^{// synchronize tasks // code snippet}); Dispatch_async (queue, ^{// perform tasks asynchronously // code snippet});Copy the code

Queue (Dispatch Queue)

Queue: A queue is a waiting queue for tasks to be executed. A queue is a special kind of linear table, using FIFIO(first in, first out) principle.

GCD queues are classified into two types: serial queues and parallel queues. The main differences between GCD queues are as follows: The execution sequence is different and the number of threads is different.

  • Serial queue (Serial Dispatch Queue): Only one task is executed at a time. Allow tasks to be executed one after another (only one thread is started, and the next task is executed after each task is completed).
  • Concurrent queue (Concurrent Dispatch Queue): Allows multiple tasks to be executed concurrently (simultaneously).

Note:The concurrency functionality of concurrent queues is only available in asynchronous (dispatch_async) method. Other threads execute tasks sequentially

Dispatch_queue_t dispatch_queue_CREATE (const char *_Nullable label, dispatch_queue_attr_t _Nullable attr);

  • Parameter 0: the unique identifier of the queue. The name of the queue is recommended to use the reversed full domain name of the application ID
  • Parameter 1: Identifies whether the queue is serial or concurrent (DISPATCH_QUEUE_SERIAL.DISPATCH_QUEUE_CONCURRENT)
// dispatch_queue_t serialQueue = dispatch_queue_create("com.appleid.functionA", DISPATCH_QUEUE_SERIAL); // dispatch_queue_t concurrentlQueue = dispatch_queue_create("com.appleid.functionB", DISPATCH_QUEUE_CONCURRENT); // dispatch_queue_t mainQueue = dispatch_get_main_queue(); // Global concurrent queue (parameter 0: default, parameter 1: Fill in 0) dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);Copy the code

Queue and synchronous and asynchronous task combination

The difference between Concurrent queue Serial queues The home side column
Synchronization (sync) No new thread is started and the task is executed sequentially No new thread is started and the task is executed sequentially The deadlock is stuck and will not be executed
Asynchronous (async) Start a new thread and execute tasks concurrently Start a new thread (1 thread) to execute tasks in serial No new thread is started and the task is executed sequentially

4 the GCD methods

4.1 dispatch_after: Indicates delayed execution

Main: the dispatch_after method does not start processing after a specified time, but appends tasks to the main queue after a specified time. This is not an exact time, but the dispatch_after method is useful for roughly delaying the execution of tasks

- (void)after {NSLog(@" currentThread %@", [NSThread currentThread]); Dispatch_after (dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{NSLog(@" thread after:%@", [NSThread currentThread]); // Prints the current thread}); }Copy the code

4.2 dispatch_once: This command is executed only once

You can use the dispatch_once method when creating singletons or when you have code that is executed only once during the entire program run. The dispatch_once method ensures that a piece of code is executed only once during the program run, and even in a multithreaded environment, Dispatch_once also ensures thread safety.

- (void)once { static dispatch_once_t onceToken; Dispatch_once (&oncetoken, ^{// only execute once, default thread safe // code snippet}); }Copy the code

4.3 dispatch_barrier_async: fence function

Apple Official Documents

The call to this function always returns immediately after the block is committed and never waits for the block to be called. When a Barrier Block reaches the front of a private concurrent queue, it does not execute immediately. Instead, the queue waits until the currently executing block completes execution. At this point, the Barrier Block executes itself. Any blocks committed after the Barrier block are not executed until the Barrier block is complete.

The queue you specify should be a concurrent queue that you create yourself using the dispatch_queue_CREATE function. If the queue passed to this function is a serial queue or a global concurrent queue, this function behaves like the dispatch_async function.

  • dispatch_barrier_sync
- (void)barrier { dispatch_queue_t queue = dispatch_queue_create("com.appleid.functionA", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@" task 1, %@", [NSThread currentThread]); }); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:1]; NSLog(@" task 2, %@", [NSThread currentThread]); }); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@" task 3, %@", [NSThread currentThread]); }); dispatch_barrier_async(queue, ^{ [NSThread sleepForTimeInterval:2- (void)barrier { dispatch_queue_t queue = dispatch_queue_create("com.appleid.functionA", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{[NSThread sleepForTimeInterval:2]; NSLog(@" task 1, %@", [NSThread currentThread]);}); dispatch_async(queue, ^{[NSThread sleepForTimeInterval:1]; NSLog(@" task 2, %@", [NSThread currentThread]);}); dispatch_async(queue, ^{[NSThread sleepForTimeInterval:2]; NSLog(@" task 3, %@", [NSThread currentThread]);}); dispatch_barrier_sync(queue, ^{[NSThread sleepForTimeInterval:2]; NSLog(@" task 4 barrier, %@", [NSThread currentThread]);}); dispatch_async(queue, ^{[NSThread sleepForTimeInterval:2]; NSLog(@" task 5, %@", [NSThread currentThread]);}); dispatch_async(queue, ^{[NSThread sleepForTimeInterval:2]; NSLog(@" task 6, %@", [NSThread currentThread]);}); NSLog(@" task 7, %@", [NSThread currentThread]); }]; NSLog(@"barrier task 4, %@", [NSThread currentThread]); }); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@" task 5, %@", [NSThread currentThread]); }); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@" task 6, %@", [NSThread currentThread]); }); }Copy the code

Execution Result:

<NSThread: 0x139040570>{number = 4, name = (null)} 0x139458a90>{number = 6, name = (null)} Task 1, <NSThread: 0x139043ce0>{number = 5, name = (null)} Task 4 barrier, <NSThread: 0x137e0B8d0 >{number = 1, name = main} Task 7, <NSThread: 0x137e0B8d0 >{number = 1, name = main} Task 5, <NSThread: 0x139043CE0 >{number = 5, name = (null)} Task 6, <NSThread: 0x139458a90>{number = 6, name = (null)}Copy the code
  • dispatch_barrier_async
- (void)barrier { dispatch_queue_t queue = dispatch_queue_create("com.appleid.functionA", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@" task 1, %@", [NSThread currentThread]); }); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:1]; NSLog(@" task 2, %@", [NSThread currentThread]); }); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@" task 3, %@", [NSThread currentThread]); }); dispatch_barrier_async(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@" barrier, %@", [NSThread currentThread]); }); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@" task 5, %@", [NSThread currentThread]); }); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@" task 6, %@", [NSThread currentThread]); }); NSLog(@" task 7, %@", [NSThread currentThread]); }Copy the code

Execution Result:

Task 7, <NSThread: 0x10360AEA0 >{number = 1, name = main} Task 2, <NSThread: 0x1035a4f90>{number = 3, name = (null)} Task 1, <NSThread: 0x105E79130 >{number = 6, name = (null)} Task 3, <NSThread: 0x1036AFae0 >{number = 5, name = (null)} Task 4 Barrier, <NSThread: 0x1036AFae0 >{number = 5, name = (null)} Task 5, <NSThread: 0x1036AFAE0 >{number = 5, name = (null)} Task 6, <NSThread: 0x105E79130 >{number = 6, name = (null)}Copy the code

4.4 Dispatch_apply: Rapid Iteration (Efficient for Loop)

Apple Official Documents

This function submits multiple called blocks to the scheduling queue and waits for all iterations of the task block to complete before returning. The block can be called concurrently if the destination queue is a concurrent queue returned by dispatch_get_global_queue, so it must be Reentrant safe. Using this function in concurrent queues can be an effective parallel for loop.

The current index of the iteration is passed to each call to the block.

- (void)apply { dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // dispatch_apply(10, queue, ^(size_t index) {NSLog(@" %zu %@", index, [NSThread currentThread]); }); // Dispatch_async (dispatch_get_global_queue(0, 0), ^{dispatch_apply(10, queue, 0) ^(size_t index) {NSLog(@" async index:%zu %@", index, [NSThread currentThread]); }); }); }Copy the code

4.5 Dispatch_group: queue group

  • The dispatch_group_async invocation of the queue group places the task in the queue and then the queue in the queue group. Or use the dispatch_group_Enter or dispatch_group_leave combination of the queue group

  • Call dispatch_group_notify of the queue group to return to the specified thread to execute the task. Or use dispatch_group_wait to return to the current thread and continue (blocking the current thread)

  • Dispatch_group_notify: monitors task completion status of the group. After all tasks are complete, add tasks to the group and execute tasks

- (void)group { dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [NSThread sleepForTimeInterval:2]; NSLog(@" task 1, %@", [NSThread currentThread]); }); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [NSThread sleepForTimeInterval:2]; NSLog(@" task 2, %@", [NSThread currentThread]); }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSThread sleepForTimeInterval:2]; ^{NSThread sleepForTimeInterval:2] NSLog(@" task 3, %@", [NSThread currentThread]); NSLog(@"group task complete "); }); }Copy the code

Execution Result:

<NSThread: 0x281F87280 >{number = 5, name = (null)} 0x281FA0C00 >{number = 8, name = (NULL)} Task 3, <NSThread: 0x281FC4D80 >{number = 1, name = main} group The task is completeCopy the code
  • dispatch_group_wait: suspends the current thread (blocks the current thread), waiting for the specifiedgroupThe task can be continued only after the task is completed
- (void)group { dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [NSThread sleepForTimeInterval:2]; NSLog(@" task 1, %@", [NSThread currentThread]); }); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [NSThread sleepForTimeInterval:2]; NSLog(@" task 2, %@", [NSThread currentThread]); }); dispatch_group_wait(group, DISPATCH_TIME_FOREVER); NSLog(@"group task complete "); }Copy the code

Execution Result:

<NSThread: 0x2817F9540 >{number = 4, name = (null)} 0x2817FF9C0 >{number = 6, name = (null)} group The task is completeCopy the code
  • dispatch_group_enter(), dispatch_group_leave
- (void)group1 { dispatch_group_t group = dispatch_group_create(); dispatch_group_enter(group); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [NSThread sleepForTimeInterval:2]; NSLog(@" task 1, %@", [NSThread currentThread]); dispatch_group_leave(group); }); dispatch_group_enter(group); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [NSThread sleepForTimeInterval:2]; NSLog(@" task 2, %@", [NSThread currentThread]); dispatch_group_leave(group); }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSThread sleepForTimeInterval:2]; ^{NSThread sleepForTimeInterval:2] NSLog(@" task 3, %@", [NSThread currentThread]); NSLog(@"group task complete "); }); }Copy the code

Execution Result:

<NSThread: 0x281DF3e40 >{number = 4, name = (null)} 0x281DE3F80 >{number = 7, name = (NULL)} Task 3, <NSThread: 0x281DB0D80 >{number = 1, name = main} group The task is completeCopy the code

4.6 dispatch_semaphore: indicates the semaphore

Dispatch_semaphore is a GCD Semaphore that holds count signals. Dispatch_semaphore is a GCD Semaphore that holds count signals. Dispatch_semaphore is a GCD Semaphore that holds count signals. If the count is 0 or greater than 0, it can pass.

Main uses:

  • Keep thread synchronization and convert asynchronous execution tasks to synchronous execution tasks
  • Keep the thread safe and lock the thread

Dispatch_semaphore three methods:

  • dispatch_semaphore_create: Create asemaphoreAnd initialize the total number of signals
  • dispatch_semaphore_signal: Sends a signal, the signal counts+ 1
  • dispatch_semaphore_wait: can make the total semaphore- 1, total amount of signalLess than zero“, it waits (blocking thread), otherwise it can execute normally
- (void)semaphor {NSLog(@" currentThread :%@", [NSThread currentThread]); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:2]; NSLog(@" task 1, %@", [NSThread currentThread]); dispatch_semaphore_signal(semaphore); }); NSLog(@" currentThread 1:%@", [NSThread currentThread]); dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@" task completed "); }Copy the code

Execution Result:

<NSThread: 0x282784D80 >{number = 1, name = main} 0x282784D80 >{number = 1, name = main} Task 1, <NSThread: 0x2827D6CC0 >{number = 6, name = (null)} Task completedCopy the code

According to the printed result, the execution process is as follows:

  • semaphoreThe initial count is 0
  • Asynchronous tasksAfter joining the queue, the execution continues without waitingdispatch_semaphore_waitMethod, semaphore counting - 1for- 1Less than0, the current thread enters the wait state
  • Task 1After the command is executed, the command is executeddispatch_semaphore_signal, semaphore counting + 1for0, the blocked thread resumes execution

GitHub-> Multithreading


If there are shortcomings, welcome to correct, if you feel good writing, remember to give a thumbs up!