iOS multi-threading of the GCD

GCD Introduction


  • 1, GCD Profile
  • 2, GCD and task queue
  • 3, the basic use of GCD
  • 4, inter-thread communication GCD
  • 5, GCD other methods (Method Fence: dispatch_barrier_async, delayed execution method: dispatch_after, single-use code (one time only): dispatch_once, fast iterative method: dispatch_apply, queues: dispatch_group, Semaphore: dispatch_semaphore)

For a basic introduction to the GCD, the article stresses that are already very detailed, and here I come to deepen copy my impression; for seniors in my article summarizes the basis of a discussion about the issue of thread deadlock.

1, GCD Profile

Grand Central Dispatch (GCD) is a relatively new solution to a multi-core programming developed by Apple. It is mainly used to optimize the multi-core processor applications, and other symmetric multi-processing system. It is a concurrent tasks performed on the basis of the thread pool patterns. First introduced in Mac OS X 10.6 Snow Leopard, it can also be used in iOS 4 and above.

Benefits of GCD as follows

  • GCD calculation may be used in parallel polynuclear
  • GCD will automatically use more CPU cores (such as dual-core, quad-core)
  • GCD will automatically manage the life cycle of the thread (the thread creation, scheduling tasks, destroying threads)
  • Programmers only need to tell GCD want to perform what tasks without writing any thread management code

2, GCD and task queue

GCD two core concepts: tasks and queues

task

Task : perform the operation is meant, in other words, the code that you execute in a thread. GCD is placed in the block

  • Synchronous execution (sync)

    • Add tasks to synchronize the specified queue before the end of the mission added, it will wait before proceeding until after the completion of the task queue inside.
    • Can only perform tasks in the current thread does not have the ability to open a new thread
  • Asynchronous execution (async)

    • Asynchronous add tasks to the specified queue, it will not do any waiting, you can continue to perform the task.
    • Can perform tasks in the new thread, the ability to open a new thread.
// 同步执行任务创建方法
dispatch_sync(queue, ^{
// 这里放同步执行任务代码
});
// 异步执行任务创建方法
dispatch_async(queue, ^{
// 这里放异步执行任务代码
});
复制代码

queue

The queue (the Dispatch Queue) : queue here refers to perform a task queue, i.e. a queue for storing tasks. A queue is a special linear form, using the principle of FIFO (First In First Out), i.e., a new task is always inserted at the end of the queue, and the read task time is always read from the head of the queue. Each read a task, a task released from the queue

  • Serial queue (Serial Dispatch Queue)
    • Only one task is executed. Let one after another task to perform. (Only open a thread after a task is finished, and then perform the next task)
  • Concurrent queue (Concurrent Dispatch Queue)
    • It allows multiple tasks concurrently (simultaneously) to perform. (You can open multiple threads, and perform tasks at the same time)

Concurrent concurrent queue function valid only in asynchronous (dispatch_async) function

Method of creating queues / acquisition method

Dispatch_queue_create can be used to create a queue, you need to pass two parameters, the first parameter represents the unique identifier of the queue for DEBUG, it can be recommended as a namespace, Dispatch Queue using the application ID reverse this whole domain; second parameters are used to identify a serial or concurrent queue queue. DISPATCH_QUEUE_SERIAL represents serial queue, DISPATCH_QUEUE_CONCURRENT represents concurrent queue.

// 串行队列的创建方法
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);
// 并发队列的创建方法
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);

复制代码

For serial queue, GCD provides a special serial queues: a main queue (Main Dispatch Queue)

  • All placed in the main tasks in the queue, the main thread will be put in execution
  • May be used dispatch_get_main_queue () to get the main queue.
// 主队列的获取方法
dispatch_queue_t queue = dispatch_get_main_queue();
复制代码

For concurrent queue, GCD provides a global default concurrent queue (Global Dispatch Queue)

You can use dispatch_get_global_queue to get. We need to pass two parameters. The first parameter indicates the priority queue, usually with DISPATCH_QUEUE_PRIORITY_DEFAULT. The second parameter is temporarily useless, you can use 0.

// 全局并发队列的获取方法
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
复制代码

Using the sync function to add to the current serial task queue, the queue will be stuck serial current (deadlock)

Concurrent function will only take effect in asynchronous function

3, the basic use of GCD

Synchronous serial queue

dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
// 追加任务1
for (int i = 0; i < 2; ++i) {
NSLog(@"1---%@",[NSThread currentThread]);
}
});

dispatch_sync(queue, ^{
// 追加任务2
for (int i = 0; i < 2; ++i) {
NSLog(@"2---%@",[NSThread currentThread]);
}
});
复制代码

According print results, synchronous serial queue that is not open a new thread, nor asynchronous execution

Asynchronous serial queue

NSLog(@"主线程:%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"1====%@",[NSThread currentThread]);      // 打印当前线程
}

});
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"2====%@",[NSThread currentThread]);      // 打印当前线程
}

});
复制代码

Results: open a new thread, serial mission

Asynchronous parallel queue

NSLog(@"主线程:%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1====%@",[NSThread currentThread]);      // 打印当前线程
}

});
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"2====%@",[NSThread currentThread]);      // 打印当前线程
}

});
复制代码

Results: open a new thread, concurrent execution of tasks. Want significant concurrent execution results, you can sleep at

sync function caused by a thread deadlock

First you have to understand the concept of synchronous and asynchronous, synchronous and asynchronous purpose is not to create a new thread if the synchronization function will return the current blocking, asynchronous function returns immediately execute the following code; a queue is a data structure queue there FIFO, LIFO, and control task execution order, as to whether to open up a new thread, because the synchronization function will wait function returns, so the current thread execution on the line, no need to waste resources and then open up a new thread, if asynchronous function, the current thread needs to return immediately and then execute down, so the function inside the task must be to open up a new thread to perform this task.

The task is put on the queue, and the thread is to perform tasks on the queue

[Question 1]: The following code is executed in the main thread, it will produce a deadlock? meeting!

NSLog(@"执行任务1");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"执行任务2");
});

NSLog(@"执行任务3");
复制代码

dispatch_sync immediately synchronized to perform tasks in the current thread

analysis:

  • 1, the main thread to perform tasks: Task 1, sync, task 3,
  • 2, the main queue: viewDidLoad, Task 2,

Wherein the main queue until the end of the 3 viewDidLoad inside the task execution tasks 2; and the main thread is finished performing the task 3 will be performed sync. Task 2 is waiting for the implementation of Task 3, Task 2 Task 3 can no longer wait for execution, resulting in deadlock

[Question 2]: The following code is executed in the main thread, it will produce a deadlock? will not!

- (void)interview02
{
 
NSLog(@"执行任务1");

dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
NSLog(@"执行任务2");
});

NSLog(@"执行任务3");

// dispatch_async不要求立马在当前线程同步执行任务
}
复制代码

Because dispatch_async synchronization is not required to immediately perform the task in the current thread, the thread will not cause deadlock

[3] problem: The following code is executed in the main thread, it will produce a deadlock? meeting!

NSLog(@"执行任务1");

dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{ // 0
NSLog(@"执行任务2");

dispatch_sync(queue, ^{ // 1
NSLog(@"执行任务3");
});

NSLog(@"执行任务4");
});

NSLog(@"执行任务5");
复制代码

Which cause a deadlock between tasks and perform tasks 4 3

[4] problem: The following code is executed in the main thread, it will produce a deadlock? will not!

- (void)interview04
{
 
NSLog(@"执行任务1");

dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{ // 0
NSLog(@"执行任务2");

dispatch_sync(queue, ^{ // 1
NSLog(@"执行任务3");
});

NSLog(@"执行任务4");
});

NSLog(@"执行任务5");
}
复制代码

4, inter-thread communication GCD

In the iOS development process, we generally carried out in the main thread UI refresh the inside, such as: click, scroll, drag and other events. We usually put some time-consuming operation on other threads, such as picture download, upload files and other time-consuming operations. And when we sometimes done in other thread when consuming operations, we need to return to the main thread, then used the communication between threads.

- (void)communication {
// 获取全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
// 获取主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue(); 

dispatch_async(queue, ^{
// 异步追加任务
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];              // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
}

// 回到主线程
dispatch_async(mainQueue, ^{
// 追加在主线程中执行的任务
[NSThread sleepForTimeInterval:2];              // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
});
});
}
复制代码

5, GCD other methods

5.1, GCD fence method: dispatch_barrier_async

In asynchronous it is that we perform some operation, we use the asynchronous operation function dispatch_barrier_async temporary synchronous operation made separately on the line, like a fence

- (void)viewDidLoad {
[super viewDidLoad];

self.queue = dispatch_queue_create("rw_queue", DISPATCH_QUEUE_CONCURRENT);

for (int i = 0; i < 10; i++) {
[self read];
[self read];
[self read];
[self write];
}
}


- (void)read {
dispatch_async(self.queue, ^{
sleep(1);
NSLog(@"read");
});
}

- (void)write
{
dispatch_barrier_async(self.queue, ^{
sleep(1);
NSLog(@"write");
});
}
复制代码

We can see the observation time when performing dispatch_barrier_async write operation is performed synchronously, asynchronously will not be the case

5.2, GCD delay execution method: dispatch_after

We often encounter such a demand: to perform a task at a specified time (eg, 3 seconds). GCD can be used to achieve the function of dispatch_after. Note that: dispatch_after function did not begin the implementation process after a specified time, but after the specified time will be appended to the main task queue. Strictly speaking, this time is not absolutely accurate, but generally you want to delay the execution of tasks, dispatch_after function is very effective.

/**
* 延时执行方法 dispatch_after
*/
- (void)after {
NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
NSLog(@"asyncMain---begin");

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2.0秒后异步追加任务代码到主队列,并开始执行
NSLog(@"after---%@",[NSThread currentThread]);  // 打印当前线程
});
}
复制代码

5.3, GCD single-use code (only once): dispatch_once

We create a singleton, or run a program when the entire process is performed only once in the code, we will use the GCD function of dispatch_once

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只执行1次的代码(这里面默认是线程安全的)
});
复制代码

5.4, ​​GCD queues: dispatch_group

Sometimes we have such a demand: namely asynchronous execution of two time-consuming task, and then when two time-consuming tasks were completed and then back to the main thread to perform the task. At this time we can use GCD queue group

  • Dispatch_group_async call queue group first task into the queue, then the queue in a queue group. Or using dispatch_group_enter queue group, dispatch_group_leave combination thereof dispatch_group_async.
  • Dispatch_group_notify call queue thread back to the specified group of tasks. Or use dispatch_group_wait back to the current thread to continue down (blocks the current thread).
  • dispatch_group_enter marked a task added to the group, once, the equivalent group in to complete the unfinished task number +1
  • dispatch_group_leave mark a task group to leave, once, the complete group corresponding to the number of tasks not performed -1
  • When the group finished in the number of tasks not performed to 0, will make dispatch_group_wait unblocked, and perform additional tasks to the dispatch_group_notify.
/**
* 队列组 dispatch_group_notify
*/
- (void)groupNotify {
NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
NSLog(@"group---begin");

dispatch_group_t group =  dispatch_group_create();

dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任务1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];              // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
}
});

dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任务2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];              // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
}
});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的异步任务1、任务2都执行完毕后,回到主线程执行下边任务
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];              // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
}
NSLog(@"group---end");
});
}
复制代码

5.5, GCD semaphore: dispatch_semaphore

Dispatch Semaphore provides three functions.

  • dispatch_semaphore_create: Create a semaphore having a value of shaping, i.e. the total signal.
  • dispatch_semaphore_signal: sending a signal, so that the total signal plus 1
  • dispatch_semaphore_wait: the total signal can be minus 1, when the total amount would have been waiting for the signal to 0 (blocked where the thread), otherwise it can be executed properly.

Reproduced in: https: //juejin.im/post/5cf28dfc6fb9a07eb15d3df8

Guess you like

Origin blog.csdn.net/weixin_33845477/article/details/91459961