多线程---GCD

一,什么是GCD?

  GCD (Grand Center Dispatch),宏大(牛逼)的中央调度。Apple开发的一个多核编程的解决方法。在iOS所有实现多线程的方案中(NSThread,NSOperation&NSOperationQueue、GCD ),GCD应该是最有魅力的,因为GCD本身是苹果公司为多核的并行运算提出的解决方案。GCD在工作时会自动利用更多的CPU内核(比如双核、四核),GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)。GCD是基于C语言的,因此我们在编写GCD相关代码的时候,面对的函数,而不是方法。如果使用GCD,完全由系统管理线程,我们不需要编写线程代码。只需定义想要执行的任务,然后添加到适当的调度队列(dispatch queue)。GCD会负责创建线程和调度你的任务,系统直接提供线程管理。

二、三种多线程的优缺点? 

  1.NSThread:

    优点:NSThread ⽐比其他两个轻量级;缺点:需要⾃自⼰己管理线程的⽣生命周期,线程同步。线程同步对数据的加锁会有⼀一定的系统开销;

    2.NSOperation&NSOperationQueue:

     优点:不需要关⼼心线程管理,数据同步的事情,可以把精⼒力放在⾃自⼰己需要执⾏行的操作上。 Cocoaoperation相关的类是NSOperation,NSOperationQueue。NSOperation是个 抽象类,使⽤用它必须⽤用它的⼦子类,可以实现它或者使⽤用它定义好的两个⼦子类: NSInvocationOperation 和 NSBlockOperation。创建NSOperation⼦子类的对象,把对象 添加到NSOperationQueue队列⾥里执⾏行。

  3.GCD: 

    Grand Central Dispatch (GCD)是Apple开发的⼀一个多核编程的解决⽅方法。在iOS4.0开始 之后才能使⽤用。GCD是⼀一个替代诸如NSThread, NSOperationQueue,NSInvocationOperation等技术的很⾼高效和强⼤大的技术。现在的iOS系统都升级到6了, 所以不⽤用担⼼心该技术不能使⽤用。 

三,概念:

  GCD中有2个核心概念

  (1)任务:执行什么操作

  (2)队列:用来存放任务

   GCD的使用就2个步骤:(1)定制任务(2)确定想做的事情

 

     1. 将任务添加到队列中,GCD会自动将队列中的任务取出,放到对应的线程中执行。

     2. 任务的取出遵循队列的FIFO原则:先进先出,后进后出。

   3.添加的异步任务会创建子线程,在子线程执行;而并发任务在主线程中执行。

   4.同步任务中延时会阻塞该任务所在的线程。

四、使用示例

  1.添加自定义队列;

    //创建并发队列
    dispatch_queue_t user= dispatch_queue_create("user", DISPATCH_QUEUE_CONCURRENT);
    //创建串行队列
    //dispatch_queue_t user= dispatch_queue_create("user", DISPATCH_QUEUE_CONCURRENT);
    //添加异步任务
    dispatch_async(user, ^{
        [NSThread sleepForTimeInterval:1];
        printf("2");
    });

  2.全局(并行)队列;

 /**
     *  得到全局队列(并发队列)
     *
     *  @param DISPATCH_QUEUE_PRIORITY_DEFAULT 队列的优先级
     *  @param 0                               留作将来使用。传递其他任何非零值可能会导致一个NULL返回值。
     *
     *  @return 返回请求的全局队列,如果所请求的全局队列不存在则返回NULL。
     */
    dispatch_queue_t queue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    printf("0\n");
    dispatch_async(queue, ^{
        dispatch_sync(queue, ^{
            [NSThread sleepForTimeInterval:5];
            printf("1\n");
        });
        dispatch_async(queue, ^{
            printf("2\n");
        });
        printf("3\n");
    });
    printf("4\n");

输出结果为0 4 1 3 2 ,因为1所在的任务为同步任务,阻塞了其所在的子线程。

  3.组队列;

    /**
     *  ——labe的值从1依次变为99
     *
     *  @return
     */
    dispatch_group_t group=dispatch_group_create();
    dispatch_queue_t queue=dispatch_queue_create("user", DISPATCH_QUEUE_SERIAL);
    for (int i=0; i<100; i++) {
        dispatch_group_async(group, queue, ^{
            [NSThread sleepForTimeInterval:0.1];
            dispatch_async(dispatch_get_main_queue(), ^{
                 _lab.text=[NSString stringWithFormat:@"%d",i];
            });
           
        });
    }
   //为对象设置目标队列
   // dispatch_set_target_queue(queue, dispatch_get_main_queue());
    dispatch_group_notify(group, queue, ^{
        printf("OK");
    });

 

有些变量只需要初始化一次(如从文件中读取配置参数,读取设备型号等等),可以使用dispatch_once来进行读取优化,保证只调用API一次,以后就只要直接访问变量即可

范例如下:

 

[objc] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. static BOOL isTestMode;  
  2.   
  3. + (BOOL)isTestMode  
  4. {  
  5.     static dispatch_once_t onceToken;  
  6.     dispatch_once(&onceToken, ^{  
  7.         NSNumber* obj = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFResourceTest"];  
  8.         isTestMode = [obj boolValue];  
  9.     });  
  10.      
  11.     return isTestMode;  
  12. }  

 

 

无论是爱还是恨,你都需要单例。实际上每个iOS或Mac OS应用都至少会有UIApplication或NSApplication.
什么是单例呢?Wikipedia是如此定义的:
在软件工程中,单例是一种用于实现单例的数学概念,即将类的实例化限制成仅一个对象的设计模式。
或者我的理解是:
单例是一种类,该类只能实例化一个对象。
    尽管这是单例的实际定义,但在Foundation框架中不一定是这样。比如NSFileManger和NSNotificationCenter,分别通过它们的类方法defaultManager和defaultCenter获取。尽管不是严格意义的单例,这些类方法返回一个可以在应用的所有代码中访问到的类的共享实例。在本文中我们也会采用该方法。
    使用Objective-C实现单例模式的最佳方式向来有很多争论,开发者(包括Apple在内)似乎每几年就会改变他们的想法。当Apple引入了Grand Central Dispatch (GCD)(Mac OS 10.6和iOS4.0),他们也引入了一个很适合用于实现单例模式的函数。
    该函数就是dispatch_once:
void dispatch_once( dispatch_once_t *predicate, dispatch_block_t block);
    该函数接收一个dispatch_once用于检查该代码块是否已经被调度的谓词(是一个长整型,实际上作为BOOL使用)。它还接收一个希望在应用的生命周期内仅被调度一次的代码块,对于本例就用于shared实例的实例化。
dispatch_once不仅意味着代码仅会被运行一次,而且还是线程安全的,这就意味着你不需要使用诸如@synchronized之类的来防止使用多个线程或者队列时不同步的问题。
    Apple的GCD Documentation证实了这一点:
如果被多个线程调用,该函数会同步等等直至代码块完成。
    实际要如何使用这些呢?
    好吧,假设有一个AccountManager类,你想在整个应用中访问该类的共享实例。你可以按如下代码简单实现一个类方法:
+ (AccountManager *)sharedManager { 
    static AccountManager *sharedAccountManagerInstance = nil; 
 static dispatch_once_t predicate;
 dispatch_once(&predicate, ^{       
          sharedAccountManagerInstance = [[self alloc] init]; 
     });
    return sharedAccountManagerInstance; 
}
    这就意味着你任何时候访问共享实例,需要做的仅是:
AccountManager *accountManager = [AccountManager sharedManager];
    就这些,你现在在应用中就有一个共享的实例,该实例只会被创建一次。
    该方法有很多优势: 
           1 线程安全
           2 很好满足静态分析器要求
           3 和自动引用计数(ARC)兼容 
           4 仅需要少量代码
    该方法的劣势就是它仍然运行创建一个非共享的实例:
AccountManager *accountManager = [[AccountManager alloc] init];
    有些时候你希望有这种行为,但如果正在想要的是仅一个实例被实例化就需要注意这点。

posted on 2015-04-09 17:32  松声竹影  阅读(242)  评论(0编辑  收藏  举报

导航