GCD之串行队列和并行队列及dispatch_group

iOS_多线程技术

1. NSThread 每个NSThread对象对应一个线程,量级较轻(真正的多线程)
2.以下两点是苹果专门开发的“并发”技术,使得程序员可以不再去关心线程的具体使用问题
   1>NSOperation/NSOperationQueue 面向对象的线程技术
   2>GCD —— Grand Central Dispatch(派发) 是基于C语言的框架,可以充分利用多核,是苹果推荐使用的多线程技术  

以上这三种编程方式从上到下,抽象度层次是从低到高的,抽象度越高的使用越简单,也是Apple最推荐使用的,在项目中很多框架技术分别使用了不同多线程技术。                 

GCD_Grand Central Dispatch

GCD全称Grand Central Dispatch  是苹果公司提供的可以利用多核特性的API. 你只需要写下你的代码, 把它指派为一个队列,系统就会执行它了. 你可以异步或同步执行任意的代码 。GCD在iOS4.0+才能使用,是替代NSThread, NSOperation的高效和强大的技术

Dispatch queue_调度队列(dispatch_queue)

 它与线程相似但是用起来更加简单.无需我们注重太多的细节!

 GCD的所有的调度队列都是先进先出队列,因此队列中的任务的开始的顺序和添加到队列中的顺序相同.我们可以将部分独立运行的任务添加到队列,由系统管理执行.

 队列类型一共有三种:

 连续队列:  每个队列都会根据指派的顺序执行任务.你可以按自己的想法创建任意数量的队列,它们会并行操作任务.


 并发队列: 每个并发队列都能并发执行一个或多个任务.任务会根据指派到队列的顺序开始执行. 你无法创建连续队列,只能从系统提供的3个队列内选择一个来使用.

   

 主队列(也可以称为串行队列): 应用程序中有序的主队列,执行的是应用程序的主线程任务.


Dispatch queue_调度队列的创建

  1>  自定义串行队列,提交同步任务

  1.  //创建串行队列、提交同步任务
        dispatch_queue_t queue = dispatch_queue_create("queueName", DISPATCH_QUEUE_SERIAL);
        
        dispatch_sync(queue, ^{
            //code 任务一
        });
        dispatch_sync(queue, ^{
            //code 任务二
        });
    

      队列中的任务是同步出列的,任务一执行结束后执行任务二。这种类型的任务与主线程是同步的,会阻塞主线程


    1.       自定义串行队列,提交异步任务

      //创建串行队列、提交异步任务
          dispatch_queue_t queue = dispatch_queue_create("queueName", DISPATCH_QUEUE_SERIAL);
          
          dispatch_async(queue, ^{
              //code 任务一
          });
          
          dispatch_async(queue, ^{
              //code 任务二
          });
      

        队列的任务是同步出列,任务一执行结束后执行任务二。该类型的任务与主线程是并发执行的,不会阻塞主线程 


      1.   2> 自定义并行队列,提交同步任务

        // 创建并行队列、提交同步任务
            dispatch_queue_t queue = dispatch_queue_create("queueName", DISPATCH_QUEUE_CONCURRENT);
            dispatch_async(queue, ^{
                //code 任务一
            });
            dispatch_async(queue, ^{
                //code 任务二
            });
        

          队列的任务是异步出列,任务的出列顺序按先进先出的顺序执行,既任务一出列后任务二接着出列(任务一与任务二又是同步的),与主线程同步,会阻塞主线程

      2.     自定义并行队列,提交异步任务

        //创建并行队列、提交异步任务
            dispatch_queue_t queue = dispatch_queue_create("queueName", DISPATCH_QUEUE_CONCURRENT);
            dispatch_async(queue, ^{
                //code 任务一
            });
            dispatch_async(queue, ^{
                //code 任务一
            });
        

          任务一出列后任务二才可以出列,各任务之间是异步的,不会阻塞主线程

      3.     3>在主队列提交同步任务(也是串行队列)
        //在主队列提交同步任务
            dispatch_sync(dispatch_get_main_queue(), ^{
                //code
            });
        

          阻塞主线程

      4.         在主队列提交异步任务
        //在主队列提交异步任务
            dispatch_async(dispatch_get_main_queue(), ^{
                //code任务
            });
        

         

            4>在全局队列提交同步任务(也是并发队列)

      5.  //在全局队列提交同步任务
         dispatch_sync(dispatch_get_global_queue(0, 0), ^{
             //code
         });
        

          在全局队列提交异步任务

      6. //在全局队列提交异步任务
            dispatch_async(dispatch_get_global_queue(0, 0), ^{
                //code
            });
        

          

        dispatch_get_gloabal_queue 的第一个参数为枚举类型(默认为0),决定任务的优先级 ,第二个参数为Apple保留参数,传0
        #define DISPATCH_QUEUE_PRIORITY_HIGH        2
        #define DISPATCH_QUEUE_PRIORITY_DEFAULT     0
        #define DISPATCH_QUEUE_PRIORITY_LOW         (-2)
        #define DISPATCH_QUEUE_PRIORITY_BACKGROUND  INT16_MIN(后台任务,优先级最低)


        Dispatch Queues的生成可以有这几种方式:  
          
        1. dispatch_queue_t queue = dispatch_queue_create("com.dispatch.serial", DISPATCH_QUEUE_SERIAL);

        //生成一个串行队列,队列中的block按照先进先出(FIFO)的顺序去执行,实际上为单线程执行。第一个参数是队列的名称,在调试程序时会非常有用,所有尽 量不要重名了。    

        2. dispatch_queue_t queue = dispatch_queue_create("com.dispatch.concurrent", DISPATCH_QUEUE_CONCURRENT); 

        //生成一个并发执行队列,block被分发到多个线程去执行  


        3. dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 

        //获得程序进程缺省产生的并发队列,可设定优先级来选择高、中、低三个优先级队列。由于是系统默认生成的,所以无法调用dispatch_resume()和dispatch_suspend()来控制执行继续或中断。需要注意的是,三个队列不代表三个线程,可能会有更多的线程。并发队列可以根据实际情况来自动产生合理的线程数,也可理解为dispatch队列实现了一个线程池的管理,对于程序逻辑是透明的。  
          
        官网文档解释说共有三个并发队列,但实际还有一个更低优先级的队列,设置优先级为DISPATCH_QUEUE_PRIORITY_BACKGROUND。Xcode调试时可以观察到正在使用的各个dispatch队列。  
        4. dispatch_queue_t queue = dispatch_get_main_queue(); 

        //获得主线程的dispatch队列,实际是一个串行队列。同样无法控制主线程dispatch队列的执行继续或中断。  
          
        使用dispatch_async或dispatch_sync函数来加载需要运行的block。  
         dispatch_async(queue, ^{  
           //block具体代码  
         }); 

        //异步执行block,函数立即返回  
         dispatch_sync(queue, ^{  
          //block具体代码  
         }); 

        //同步执行block,函数不返回,一直等到block执行完毕。编译器会根据实际情况优化代码,所以有时候你会发现block其实还在当前线程上执行,并没用产生新线程。  
          实际编程经验告诉我们,尽可能避免使用dispatch_sync,嵌套使用时还容易引起程序死锁。  
          如果queue1是一个串行队列的话,这段代码立即产生死锁:  
           dispatch_sync(queue1, ^{  
            dispatch_sync(queue1, ^{  
            ......  

         });  

             ......  
          });   
         那实际运用中,一般可以用dispatch这样来写,常见的网络请求数据多线程执行模型:  
         dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
         //子线程中开始网络请求数据  
         //更新数据模型  
          dispatch_sync(dispatch_get_main_queue(), ^{  
             //在主线程中更新UI代码  

          });  
          });  
        程序的后台运行和UI更新代码紧凑,代码逻辑一目了然。  

        dispatch队列是线程安全的,可以利用串行队列实现锁的功能。比如多线程写同一数据库,需要保持写入的顺序和每次写入的完整性,简单地利用串行队列即可实现:  

        dispatch_queue_t queue1 = dispatch_queue_create("com.dispatch.writedb", DISPATCH_QUEUE_SERIAL);  
          
        - (void)writeDB:(NSData *)data  
          
        {  
          dispatch_async(queue1, ^{  
           //write database  

         });  
        }   
          
        下一次调用writeDB:必须等到上次调用完成后才能进行,保证writeDB:方法是线程安全的。   

        dispatch队列还实现其它一些常用函数,包括:  
        void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t)); //重复执行block,需要注意的是这个方法是同步返回,也就是说等到所有block执行完毕才返回,如需异步返回则嵌套在dispatch_async中来使用。多个block的运行是否并发或串行执行也依赖queue的是否并发或串行。  
          
        void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block); //这个函数可以设置同步执行的block,它会等到在它加入队列之前的block执行完毕后,才开始执行。在它之后加入队列的block,则等到这个block执行完毕后才开始执行。  
          
        void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block); //同上,除了它是同步返回函数  
          
        void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block); //延迟执行block  
          
        最后再来看看dispatch队列的一个很有特色的函数:  
          
        void dispatch_set_target_queue(dispatch_object_t object, dispatch_queue_t queue);  
          
        它会把需要执行的任务对象指定到不同的队列中去处理,这个任务对象可以是dispatch队列,也可以是dispatch源(以后博文会介绍)。而且这个过程可以是动态的,可以实现队列的动态调度管理等等。比如说有两个队列dispatchA和dispatchB,这时把dispatchA指派到dispatchB:  
          
        dispatch_set_target_queue(dispatchA, dispatchB);  
          
        那么dispatchA上还未运行的block会在dispatchB上运行。这时如果暂停dispatchA运行:  
          
        dispatch_suspend(dispatchA);  
          
        则只会暂停dispatchA上原来的block的执行,dispatchB的block则不受影响。而如果暂停dispatchB的运行,则会暂停dispatchA的运行。

      GCD_总结

        队列的类型决定了队列任务的执行方式(主队列是一个串行队列)。一般把会阻塞主线程的任务提交到异步并行队列当中。











  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值