iOS - GCD simple usage

Before learning simple to use GCD API we briefly look at the basic concepts.

Synchronous and asynchronous

Synchronous execution (sync):

  • Add tasks to synchronize the specified queue before the end of the mission added, 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.

Note: The asynchronous execution (async) Although the capacity of the new thread has turned, but not necessarily open a new thread. This type of queue with tasks as assigned (below speak).

Parallel | Concurrent and serial

  • Parallel: Refers to the occurrence of multiple tasks at the same time. Multi-core CPU simultaneously open multiple threads for multiple tasks performed simultaneously without disturbing each other.

  • Concurrency: refers to the occurrence of multiple tasks at the same time interval. Popular, is one of several tasks alternately in a thread.

  • Serial: refers to multiple tasks one after the other in one thread of execution.

queue

Here Insert Picture Description

  • Is used to store a task queue, a total of two queues: queue serial and parallel queues (major difference is that: different execution order, and different numbers of threads open)
  • Serial 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)
    • On the serial task queue, the GCD will FIFO (First In First Out) to take out one of a performing, then remove one of a performing such aHere Insert Picture Description
  • Parallel queue

    • Concurrent queue (Concurrent Dispatch Queue):
      allows multiple tasks concurrently (simultaneously) to perform. (You can open multiple threads, and perform tasks at the same time) Note: concurrent function concurrent queue valid only in asynchronous (dispatch_async) function
      Here Insert Picture Description

Official Documents

Developers want to do is define the tasks performed and added to the appropriate Dispatch Queue in. Dispatch Queue queue processing is performed. We can dispatch-async such as API, written in block syntax want to perform processing and append it to the Dispatch Queue, Dispatch Queue is processed, first in first out (FIFO) in the order added.
There are two in the implementation process Dispatch Queue, one is serial Serial Dispatch Queue scheduling queue, waiting for the end of this event is now in process execution. The other is the Concurrent Dispatch Queue concurrent dispatch queue, this is not waiting for the end to perform event handling now.

GCD use

GCD step is very simple to use, just two steps.

1 creates a queue (queue serially or concurrently queue)
2 task added to the task waiting queue, the system will then (synchronous or asynchronous execution performed) according to execute task type
lower method of creating a look queue / acquisition method , and how to create a task.

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.

    • 1). Dispatch_queue_create function generation Dispatch Queue

       // 同步执行任务创建方法
        dispatch_sync(dispatch_queue_t  _Nonnull queue, ^{
       // 执行的代码
       });
       //异步执行任务的创建方法
       	 dispatch_async(dispatch_queue_t  _Nonnull queue, ^{
       // 执行的代码
       });
      
    • 2).使用系统提供的Dispatch Queue。

      • 对于串行队列,GCD 提供了的一种特殊的串行队列:主队列(Main Dispatch Queue)。
        所有放在主队列中的任务,都会放到主线程中执行。
        可使用dispatch_get_main_queue()获得主队列。

          //主队列的获取方法
             dispatch_queue_t queue = dispatch_get_main_queue();
        
      • 对于并发队列,GCD 默认提供了全局并发队列(Global Dispatch Queue)。
        可以使用dispatch_get_global_queue来获取。需要传入两个参数。第一个参数表示队列优先级,一般用DISPATCH_QUEUE_PRIORITY_DEFAULT。第二个参数暂时没用,用0即可。

          该方法的第一个参数是执行的优先级,系统提供了
          DISPATCH_QUEUE_PRIORITY_DEFAULT. (默认优先级)
          DISPATCH_QUEUE_PRIORITY_BACKGROUND。(后台优先级)
          DISPATCH_QUEUE_PRIORITY_HIGH。 (高优先级)
          DISPATCH_QUEUE_PRIORITY_LOW。 (低优先级)
        

        四个参数供选择,其中后台优先级表示用户不需要知道任务什么时候完成,如果选择这个选项速度会特别慢,不利于调试,不建议多使用。第二个参数是个标记,是为了未来使用保留的,建议这个参数设置为0。只要是并行任务一般都加入到这个队列。这是系统提供的一个全局并发队列。

任务的创建方法

GCD 提供了同步执行任务的创建方法dispatch_sync和异步执行任务创建方法dispatch_async。

  • 同步任务(dispatch_sync)
    不会另开线程,会阻塞当前线程 (SYNC)。
    dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);

      // 同步执行任务创建方法
      dispatch_sync(queue, ^{
      // 这里放同步执行任务代码
      });
    

+异步任务(dispatch_async)
会另开线程,不会阻塞当前线程 (ASYNC)。
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

	// 异步执行任务创建方法
	dispatch_async(queue, ^{
    // 这里放异步执行任务代码
	});

GCD之间的通信

在iOS开发过程中,我们一般在主线程里边进行UI刷新,例如:点击、滚动、拖拽等事件。我们通常把一些耗时的操作放在其他线程,比如说图片下载、文件上传等耗时操作。而当我们有时候在其他线程完成了耗时操作时,需要回到主线程,那么就用到了线程之间的通讯。

- (void)communication {
    // 获取全局并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    // 获取主队列
    //dispatch_queue_t mainQueue = dispatch_get_main_queue();
    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]);      // 打印当前线程
        });
         NSLog(@"1234");
    });
    NSLog(@"123");
}

输出结果如下Here Insert Picture Description
这里回到主线程这里用的是async 就其实在想用sync是否可以就其实有点想看你这里的需求,如果你想要的是回到主线程后在进行1234的打印然后就可以用sync而如果没有就一般用sync。

GCD 的其他方法

GCD 栅栏方法 dispatch_barrier_async

  • 我们有时需要异步执行两组操作,而且第一组操作执行完之后,才能开始执行第二组操作。这样我们就需要一个相当于栅栏一样的一个方法将两组异步执行的操作组给分割起来,当然这里的操作组里可以包含一个或多个任务。这就需要用到dispatch_barrier_async方法在两个操作组间形成栅栏。
    Here Insert Picture Description dispatch_barrier_async函数会等待前边追加到并发队列中的任务全部执行完毕之后,再将指定的任务追加到该异步队列中。然后在dispatch_barrier_async函数追加的任务执行完毕之后,异步队列才恢复为一般动作,接着追加任务到该异步队列并开始执行。具体如下图所示:
    /**
  • 栅栏方法 dispatch_barrier_async
    */

     - (void)barrier {
     dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
     
     dispatch_async(queue, ^{
         // 追加任务1
         for (int i = 0; i < 2; ++i) {
             [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
             NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
         }
     });
     dispatch_async(queue, ^{
         // 追加任务2
         for (int i = 0; i < 2; ++i) {
             [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
             NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
         }
     });
     
     dispatch_barrier_async(queue, ^{
         // 追加任务 barrier
         for (int i = 0; i < 2; ++i) {
             [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
             NSLog(@"barrier---%@",[NSThread currentThread]);// 打印当前线程
         }
     });
     
     dispatch_async(queue, ^{
         // 追加任务3
         for (int i = 0; i < 2; ++i) {
             [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
             NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
         }
     });
     dispatch_async(queue, ^{
         // 追加任务4
         for (int i = 0; i < 2; ++i) {
             [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
             NSLog(@"4---%@",[NSThread currentThread]);      // 打印当前线程
         }
     });
    

打印如下Here Insert Picture Description

GCD 延时执行方法:dispatch_after

我们经常会遇到这样的需求:在指定时间(例如3秒)之后执行某个任务。可以用 GCD 的dispatch_after函数来实现。
需要注意的是:dispatch_after函数并不是在指定时间之后才开始执行处理,而是在指定时间之后将任务追加到主队列中。严格来说,这个时间并不是绝对准确的,但想要大致延迟执行任务,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]);  // 打印当前线程
    });
}

Here Insert Picture Description

GCD 一次性代码(只执行一次):dispatch_once

我们在创建单例、或者有整个程序运行过程中只执行一次的代码时,我们就用到了 GCD 的 dispatch_once 函数。使用
dispatch_once 函数能保证某段代码在程序运行过程中只被执行1次,并且即使在多线程的环境下,dispatch_once也可以保证线程安全。

/**
 * 一次性代码(只执行一次)dispatch_once
 */
- (void)once {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 只执行1次的代码(这里面默认是线程安全的)
    });
}

如下
Here Insert Picture Description

GCD 快速迭代方法:dispatch_apply

  • 通常我们会用 for 循环遍历,但是 GCD 给我们提供了快速迭代的函数dispatch_apply。dispatch_apply按照指定的次数将指定的任务追加到指定的队列中,并等待全部队列执行结束。
    如果是在串行队列中使用 dispatch_apply,那么就和 for 循环一样,按顺序同步执行。可这样就体现不出快速迭代的意义了。
    我们可以利用并发队列进行异步执行。比如说遍历 0~5 这6个数字,for 循环的做法是每次取出一个元素,逐个遍历。dispatch_apply 可以 在多个线程中同时(异步)遍历多个数字。
    还有一点,无论是在串行队列,还是异步队列中,dispatch_apply 都会等待全部任务执行完毕,这点就像是同步操作,也像是队列组中的 dispatch_group_wait方法。

      - (void)apply {
      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
      
      NSLog(@"apply---begin");
      dispatch_apply(10, queue, ^(size_t index) {
          NSLog(@"%zd---%@",index, [NSThread currentThread]);
      });
      NSLog(@"apply---end");
    

    }
    Here Insert Picture Description

GCD 队列组:dispatch_group

有时候我们会有这样的需求:分别异步执行2个耗时任务,然后当2个耗时任务都执行完毕后再回到主线程执行任务。这时候我们可以用到 GCD 的队列组。

  • 调用队列组的 dispatch_group_async 先把任务放到队列中,然后将队列放入队列组中。或者使用队列组的 dispatch_group_enter、dispatch_group_leave 组合 来实现
    dispatch_group_async。

    • dispatch_group_wait. pauses the current thread (block the current thread), wait for the task to perform the specified group after the completion, will continue down.

        - (void)groupWait {
            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_enter, dispatch_group_leavedispatch_group_enter marks group appended to a task, execution time, execution is not complete in the group corresponding to the number of tasks + 1'd
      dispatch_group_leave mark a task group to leave, once, corresponding to the number of group is not finished executing task 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 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_notify

  • Monitor completion of the task group in the state, after all tasks are executed, an additional task to the group and perform tasks.

      	/**
       * 队列组 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");
          });
      }
    

This article Reference article

Published 34 original articles · won praise 4 · Views 719

Guess you like

Origin blog.csdn.net/weixin_44824650/article/details/103328254
gcd