简单的GCD学习笔记

2017-08-24  本文已影响14人  丘山Ivan

在GCD苹果官方文档第一句话就是:

Execute code concurrently on multicore hardware by submitting work to dispatch queues managed by the system.(在多核硬件上执行分配给队列的任务)

这句话就说出了GCD的诸多优点,我们都知道GCD在iOS开发中是多线程开发中用的最多的。但是却不是像pthread和NSThread去直接操作线程的,GCD是用一个叫队列的东西来包装了线程,所以我们在开发中只需要操作好队列就可以了。完全不用去管理线程的创建和销毁。这样就大大减少了开发中遇到莫名其妙的bug。

问:我们为什么要使用多线程?
答:因为在App的运行过程中会有许多耗时的操作,比如:较大的网络图片,观看视频,听歌,书籍等资源的下载。我们需要把这些操作都放到后台的子线程上去执行,不能卡主主线程,因为我们需要提高用户体验,对App的使用者友好一点,这样我们的App才能在用户的移动设备中一直存在。

GCD核心概念:

GCD的操作步骤:
1.定制任务(我们要执行的操作)
2.将任务添加到队列中(GCD就会自动从队列中的任务取出任务放到对应的线程中执行,任务中的队列遵循FIFO原则:先进先出,后进后出)

GCD的对列分为2大类型(任务的执行方式):

GCD执行任务的方式(具不具备开启线程的能力):
1.同步:只在当前线程中执行任务,不会开启新的线程,因为同步不具备开启新线程的能力。会顺序执行。
2.异步:可以在新线程中执行人,会开启新的线程,因为它具备开启新线程的能力。只要有任务,就会去线程池取子线程(主线程除外)。

注意:
1.异步执行的方式并不是一定会开启线程,如果任务不是耗时操作且执行速度极快,就不会再开启新的线程,任务会继续在现有且未被收回线程池的线程上执行。
2.同步操作和队列无关。更新UI最好放在同步方法里面。
3.开不开线程取决于执行任务的函数,同步就不开,异步就开
4.开几条线程,取决于队列,串行开一条,并发开多条(异步)

GCD常见示例代码及方法详解:

-(void)GCDDemo1{
    //1.创建队列 - 并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    //2.任务添加到队列中
    //2.1 定义了一个任务
    void(^task)() = ^{
        NSLog(@"%@",[NSThread currentThread]);
    };
    //2.2添加任务到队列
    dispatch_sync(queue, task);
}
-(void)GCDDemo2{
    //1.创建队列 并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    //2.定义一个任务
    //2.1 定义一个任务
    void(^task)() = ^{
        NSLog(@"%@",[NSThread currentThread]);
    };
    //2.2添加方法到队列
    dispatch_async(queue, task);
}
-(void)CGDDemo3{
    //指定任务的执行方法-- 异步
    //开启了线程
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
         //耗时操作
        NSLog(@"%@",[NSThread currentThread]);//number=3
        //更新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"更新UI%@",[NSThread currentThread]);//number=1            
        });
    });
//MARK: - 利用GCD加载图片
    //异步执行
    dispatch_async(dispatch_get_global_queue(0, 0), ^{    
        NSURL *url = [NSURL URLWithString:@"我是图片链接"];
        NSData *data = [NSData dataWithContentsOfURL:url];
        UIImage *image = [UIImage imageWithData:data];       
        dispatch_async(dispatch_get_main_queue(), ^{
           //更新UI - 把image添加到控件上
        });      
    });
}
-(void)GCDDemo4{
    //队列 -- 串行 DISPATCH_QUEUE_SERIAT 串行 == NULL
    dispatch_queue_t queue = dispatch_queue_create("", NULL);
    //同步
    for (int i=0; i<10; i++) {
        dispatch_sync(queue, ^{
            NSLog(@"%@------%d",[NSThread currentThread],i);
        });
    }
    //会依次打印i的值从0到9. 线程的number = 1
}
-(void)GCDDemo5{
    //队列 -- 串行 DISPATCH_QUEUE_SERIAT 串行 == NULL
   dispatch_queue_t queue = dispatch_queue_create("我是线程的名字String", NULL);
    for (int i=0; i<10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"%@------%d",[NSThread currentThread],i);
        });
    }
    //打印顺序不一定,如果此操作耗时大于创建线程与执行子线程上任务的时间就会靠后执行。反之则靠前执行。在主线程上执行。
    NSLog(@"come here");
}
-(void)GCDDemo6{
    //队列 -- 并发 DISPATCH_QUEUE_CONCURRENT 并发
    dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
    for (int i=0; i<10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"%@------%d",[NSThread currentThread],i);
        });
    }
       //打印顺序不一定。如果此操作耗时大于创建线程与执行子线程上任务的时间就会靠后执行。反之则靠前执行。在主线程上执行。
    NSLog(@"come here");
}

并发队列 同步执行(不会开线程,顺序执行)

-(void)GCDDemo7{
        //队列 -- 并发
    dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
    for (int i=0; i<10; i++) {
        dispatch_sync(queue, ^{
            NSLog(@"%@------%d",[NSThread currentThread],i);
        });
    }
    //最后打印
    NSLog(@"come here");
}
-(void)GCDDemo8{
    dispatch_queue_t queue = dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);
    //1.eg:登录 放到同步任务中,会先执行登录。必须先完成才会执行后面的.是在主线程的。如果很耗时就会有问题。demo9解决这个问题。
    dispatch_sync(queue, ^{
        NSLog(@"登录%@",[NSThread currentThread]);
    });
     //2.eg:支付
    dispatch_async(queue, ^{
        NSLog(@"支付%@",[NSThread currentThread]);
    });
     //3.eg:下载
    dispatch_async(queue, ^{
        NSLog(@"下载%@",[NSThread currentThread]);
    });   
}
-(void)GCDDemo9{
    //异步
    dispatch_queue_t queue = dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);
    //把三个任务包装成一个任务再放到异步中
    void(^task)()  = ^ {
        //1.eg:登录 放到同步任务中,会先执行登录。必须先完成才会执行后面的。
        dispatch_sync(queue, ^{
            NSLog(@"登录%@",[NSThread currentThread]);
        });
        //2.eg:支付
        dispatch_async(queue, ^{
            NSLog(@"支付%@",[NSThread currentThread]);
        });
        //3.eg:下载
        dispatch_async(queue, ^{
            NSLog(@"下载%@",[NSThread currentThread]);
        });
    };
    
    dispatch_async(queue, task);
}
-(void)GCDDemo10{
    //全局队列
    /*
     参数:
    - 参数1:涉及系统适配
        ios 8.0 服务质量
            QOS_CLASS_USER_INTERACTIVE  用户交互(希望线程快速执行,不要放耗时操作)
            QOS_CLASS_USER_INITIATED    用户需要的(不要放耗时操作)
            QOS_CLASS_DEFAULT           默认
            QOS_CLASS_UTILITY           使用工具(耗时操作)
            QOS_CLASS_BACKGROUND        后台(CPU调度次数会很少,可以忽略)
            QOS_CLASS_UNSPECIFIED       没有指定优先级
        ios 7.0
            DISPATCH_QUEUE_PRIORITY_HIGH 2          高优先级
            DISPATCH_QUEUE_PRIORITY_DEFAULT 0       默认优先级
            DISPATCH_QUEUE_PRIORITY_LOW (-2)        低优先级
            DISPATCH_QUEUE_PRIORITY_BACKGROUND      后台优先级(CPU调度次数会很少,可以忽略)
     - 参数2:未来预留参数
     
     提示:优先级不要选择后台优先级 。使用默认优先级可以适配7.0和8.0
     */
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    for (int i = 0; i< 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"%@ --- %d",[NSThread currentThread],i);
        });
    }
    //比较靠前,创建线程需要时间所以执行靠前
    NSLog(@"come here");
}
-(void)gcdDemo11{
    /*
     全局队列  &   并发队列
     1>名称,并发队列取名字,适用于企业开发,跟踪错误
     2>release 在MRC 并发队列需要使用的。全局队列不需要释放。
        dispatch_release(q);//ARC 情况下不需要release
    
     全局队列  &   串行队列
    
        全局队列:并发,能够调度多个线程,执行效率相对高。
            -  费电
        串行队列:一个一个执行,执行效率相对低
            -  省电
        判断依据:根据用户上网方式
            - wifi:可以多开线程
            - 流量 :尽量少开线程
    
     */
    //并发队列  可以取名字,适合于做企业开发跟踪错误
    dispatch_queue_t q = dispatch_queue_create("全局队列和并发队列的异同", DISPATCH_QUEUE_CONCURRENT);
   
    for (int i=0; i<10; i++) {
        dispatch_async(q, ^{
            NSLog(@"%@----%d",[NSThread currentThread],i);
        });
    }
    //全局队列
    dispatch_queue_t q1 = dispatch_get_global_queue(0, 0);
   
    for (int i=0; i<10; i++) {
        dispatch_async(q1, ^{
            NSLog(@"%@----%d",[NSThread currentThread],i);
        });
    }
   
}
#pragma mark - 延时执行
-(void)gcdDemo12{
   
    NSLog(@"come here");
   
    /**
     DISPATCH_TIME_NOW 从现在开始
     int64_t 经过多少纳秒之后
     */
    dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
    /**
     从现在开始,进过多少纳秒之后,异步执行
     参数:
     1。时间
     2.队列
     3,block
     */
    dispatch_after(when, dispatch_get_main_queue(), ^{
        NSLog(@"%@",[NSThread currentThread]);
    });
   
}
// 单例设计模式中,用的非常普遍
-(void)gcdDemo13{
    NSLog(@"来了");
    //苹果提供的一次执行机制,不仅能够保证一次执行,而且是线程安全
    static dispatch_once_t onceToken;
    //推荐使用 gcd 一次执行,效率比互斥锁的效率高
    dispatch_once(&onceToken, ^{
        //只会执行一次
        NSLog(@"执行了  %@",[NSThread currentThread]);
    });
}
-(void)gcdDemo14{
    //创建队列
   dispatch_queue_t q = dispatch_get_global_queue(0, 0);
    //创建调度组
    dispatch_group_t g = dispatch_group_create();
    //添加任务,让队列调度,任务执行情况,最后通知群组
    dispatch_group_async(g, q, ^{
        NSLog(@"下载A------%@",[NSThread currentThread]);
    });
    dispatch_group_async(g, q, ^{
        NSLog(@"下载B------%@",[NSThread currentThread]);
    });
    dispatch_group_async(g, q, ^{
        NSLog(@"下载C------%@",[NSThread currentThread]);
    });
 
    //执行完毕后,通知
    //用一个调度组,可以监听全局队列的任务,主队列取执行最后的任务
    //dispatch_group_notify这本身也是异步的(在子线程中)
    dispatch_group_notify(g, q, ^{
        NSLog(@"下载完成------%@",[NSThread currentThread]);
    });
    //在主线程中---更新UI通知用户
    dispatch_group_notify(g, dispatch_get_main_queue(), ^{
        NSLog(@"更新UI");
    });
   
}
/**
 同步任务死锁:当前是在主线程上,让主队列执行同步任务
 */
//主队列是专门负责在主线程上调度任务的队列 ---> 不会开线程
-(void)gcdDemo15{
    //1.队列  --->  一启动主线程,就可以获取主队列
    dispatch_queue_t q = dispatch_get_main_queue();
   
    //异步任务
    dispatch_async(q, ^{
        NSLog(@"%@",[NSThread currentThread]);
    });
   
    NSLog(@"come here");
}
//主线程中会锁死(在主线程上)
-(void)gcdDemo16{
    //1.队列  --->  一启动主线程,就可以获取主队列
    dispatch_queue_t q = dispatch_get_main_queue();
   
    //同步任务
    //同步执行任务的特点:这一句话不执行完毕就不能执行下一句,阻塞式的。
    dispatch_sync(q, ^{
        NSLog(@"%@",[NSThread currentThread]);
    });
   
    NSLog(@"come here");
}
//MARK: 主队列同步任务(不锁死,就要放到异步子线程上)
-(void)gcdDemo17{
    void(^task)() = ^{
        //1.队列  --->  一启动主线程,就可以获取主队列
        dispatch_queue_t q = dispatch_get_main_queue();
       
        //同步任务
        //同步执行任务的特点:这一句话不执行完毕就不能执行下一句,阻塞式的。
        dispatch_sync(q, ^{
            NSLog(@"%@",[NSThread currentThread]);
        });
       
        NSLog(@"come here");
    };
    //放到子线程上
    dispatch_async(dispatch_get_global_queue(0, 0), task);
}
 /**
     dispatch_barrier_async 作用是在并行队列中,等待前面两个操作并行操作完成,这里是并行输出
     dispatch-1,dispatch-2
     然后执行
     dispatch_barrier_async中的操作,(现在就只会执行这一个操作)执行完成后,即输出
     "dispatch-barrier,
     最后该并行队列恢复原有执行状态,继续并行执行
     dispatch-3,dispatch-4
    
     */
-(void)gcdDemo18{
   
    dispatch_queue_t concurrentQueue =dispatch_queue_create("my", NULL);
    dispatch_async(concurrentQueue, ^(){
        NSLog(@"dispatch-1");
    });
    dispatch_async(concurrentQueue, ^(){
        NSLog(@"dispatch-2");
    });
    dispatch_barrier_async(concurrentQueue, ^(){
        NSLog(@"dispatch-barrier");
    });
    dispatch_async(concurrentQueue, ^(){
        NSLog(@"dispatch-3");
    });
    dispatch_async(concurrentQueue, ^(){
        NSLog(@"dispatch-4");
    });
   
}
上一篇 下一篇

猜你喜欢

热点阅读