GCD的理解与使用

来源:互联网 发布:linux arping 编辑:程序博客网 时间:2024/06/06 01:30

GCD的理解与使用


在这之前先很通俗理解几个基本的概念:
队列(queue):队列是相对任务而言的,队列是一个存放任务的地方。
任务(task):需要做啥?把需要做的事情“打包”成一个任务,再塞入自己所能“掌握”的队列里面。
调度:对列有了,里面也有任务了。那么任务需要在什么时候被开始?


先了解一下队列的四种优先级

/** *  全局队列的四种优先级 * *  DISPATCH_QUEUE_PRIORITY_HIGH *  DISPATCH_QUEUE_PRIORITY_DEFAULT *  DISPATCH_QUEUE_PRIORITY_LOW *  DISPATCH_QUEUE_PRIORITY_BACKGROUND *  被设置成后台级别的队列,它会等待所有比它级别高的队列中的任务执行完成或者CPU空闲的时候才会执行自己的任务。 *  例如磁盘的读写操作非常耗时,如果我们不需要立即获取到磁盘的数据,我们可以把读写任务放到后台队列中,这样读写任务只会在恰当的时候去执行, *  从而让程序更加有效率。 */
#pragma mark - 只执行一次的函数,多用于单例化- (void)dispatchOnceToken {    static dispatch_once_t onceToken;    for (NSInteger i = 0; i < 10; i++) {        /// 只会在i == 0的时候进入一次,并且推出再进来,NSLog(@"%ld",i);永远不会再走了        dispatch_once(&onceToken, ^{            NSLog(@"%ld",i);        });    }}

主要用于单例化。

#pragma mark - 同步和异步- (void)asycnAction {    weakSelf(self);    /// 异步全局队列,并塞入一个任务    dispatch_async(dispatch_get_global_queue(0, 0), ^{        /// 做一个延迟操作        sleep(5);        /// 回到主线程(主线程默认是一个串行队列,所以这边用sync和async的效果是一样的)        /**         * 这边如果都使用weakself就不会产生强引用,退出当前VC之后立马dealloc         * 但是延迟操作之后,该任务还是会被执行,但是此时weakself为nil,所有任务表现为无效         *         * 如果其中一个使用self,便会产品强引用         */        dispatch_sync(dispatch_get_main_queue(), ^{            [weakSelf printNum:@"123"];        });        dispatch_async(dispatch_get_main_queue(), ^{            /// 这边使用self会产生强引用,退出当前VC之后并不会立刻dealloc            [self printNum:@"456"];        });    });}

其实,主线程(main)就是一个串行队列,也是UI的绘制线程,所以保证main线程的通畅是“代码”需要考虑深究的。

#pragma mark - 自定义队列- (void)customQueue {    /// 串行队列    /// PS:dispatch_queue_attr_t设置成NULL的时候默认代表串行。    dispatch_queue_t serialQueue;    serialQueue = dispatch_queue_create("com.example.SerialQueue", NULL);    /// 并发队列    dispatch_queue_t concurrentQueue;    concurrentQueue = dispatch_queue_create("com.example.ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);}

自定义队列,可以在需要手动管理的时候,通过自定义的队列名称,获取到相关的队列,进行挂起等操作。

#pragma mark - 挂起、恢复队列- (void)suspendAndResumeQueue {    dispatch_queue_t globaleQueue = dispatch_get_global_queue(0, 0);    /// 挂起队列。挂起操作并不会对已经开始执行的任务起作用,仅会阻止队列中还未开始的任务    dispatch_suspend(globaleQueue);    /// 恢复队列    dispatch_resume(globaleQueue);}

要慎用线程的挂起操作。

#pragma mark - 信号量的使用/** *  信号量的作用是控制多个任务对有限数量资源的访问。 *  简单来说就是,如果你创建一个有着两个资源的信号量,那么最多同时只能有两个线程可以访问临界区,其它想使用资源的线程必须在FIFO队列里面等待 */- (void)dispatchSemaphores {    weakSelf(self);    /// 创建一个信号量    dispatch_semaphore_t t = dispatch_semaphore_create(1);    dispatch_queue_t concurrentQueue1;    concurrentQueue1 = dispatch_queue_create("com.example.ConcurrentQueue1", DISPATCH_QUEUE_CONCURRENT);    dispatch_queue_t concurrentQueue2;    concurrentQueue2 = dispatch_queue_create("com.example.ConcurrentQueue2", DISPATCH_QUEUE_CONCURRENT);    dispatch_queue_t concurrentQueue3;    concurrentQueue3 = dispatch_queue_create("com.example.ConcurrentQueue3", DISPATCH_QUEUE_CONCURRENT);    /** 需要在每一个队列里面通过dispatch_semaphore_wait这个来等待信号量*/    dispatch_async(concurrentQueue1, ^{        /// 避免数据的抢夺        dispatch_semaphore_wait(t, DISPATCH_TIME_FOREVER);        for (NSInteger i = 0; i < 10; i++) {            NSLog(@"第一队列:i=%ld",i);        }        dispatch_async(dispatch_get_main_queue(), ^{            /// 使用完数据,发送一个信号            [weakSelf printNum:@"第一队列任务结束了"];            dispatch_semaphore_signal(t);        });    });    dispatch_async(concurrentQueue2, ^{        dispatch_semaphore_wait(t, DISPATCH_TIME_FOREVER);        for (NSInteger i = 0; i < 10; i++) {            NSLog(@"第二队列_____:i=%ld",i);        }        dispatch_async(dispatch_get_main_queue(), ^{            /// 使用完数据,发送一个信号            [weakSelf printNum:@"第二队列:任务结束了"];            dispatch_semaphore_signal(t);        });    });    dispatch_async(concurrentQueue3, ^{        dispatch_semaphore_wait(t, DISPATCH_TIME_FOREVER);        for (NSInteger i = 0; i < 10; i++) {            NSLog(@"第三队列——————:i=%ld",i);        }        dispatch_async(dispatch_get_main_queue(), ^{            /// 使用完数据,发送一个信号            [weakSelf printNum:@"第三队列:任务结束了"];            dispatch_semaphore_signal(t);        });    });}下面是打印结果:2017-07-03 15:17:55.109 Functions[13737:1176065] 第一队列:i=02017-07-03 15:17:55.109 Functions[13737:1176065] 第一队列:i=12017-07-03 15:17:55.110 Functions[13737:1176065] 第一队列:i=22017-07-03 15:17:55.110 Functions[13737:1176065] 第一队列:i=32017-07-03 15:17:55.110 Functions[13737:1176065] 第一队列:i=42017-07-03 15:17:55.110 Functions[13737:1176065] 第一队列:i=52017-07-03 15:17:55.110 Functions[13737:1176065] 第一队列:i=62017-07-03 15:17:55.111 Functions[13737:1176065] 第一队列:i=72017-07-03 15:17:55.111 Functions[13737:1176065] 第一队列:i=82017-07-03 15:17:55.111 Functions[13737:1176065] 第一队列:i=92017-07-03 15:17:55.126 Functions[13737:1175989] 第一队列任务结束了2017-07-03 15:17:55.126 Functions[13737:1176068] 第二队列_____:i=02017-07-03 15:17:55.126 Functions[13737:1176068] 第二队列_____:i=12017-07-03 15:17:55.126 Functions[13737:1176068] 第二队列_____:i=22017-07-03 15:17:55.127 Functions[13737:1176068] 第二队列_____:i=32017-07-03 15:17:55.127 Functions[13737:1176068] 第二队列_____:i=42017-07-03 15:17:55.127 Functions[13737:1176068] 第二队列_____:i=52017-07-03 15:17:55.127 Functions[13737:1176068] 第二队列_____:i=62017-07-03 15:17:55.127 Functions[13737:1176068] 第二队列_____:i=72017-07-03 15:17:55.127 Functions[13737:1176068] 第二队列_____:i=82017-07-03 15:17:55.128 Functions[13737:1176068] 第二队列_____:i=92017-07-03 15:17:55.128 Functions[13737:1175989] 第二队列:任务结束了2017-07-03 15:17:55.128 Functions[13737:1176107] 第三队列——————:i=02017-07-03 15:17:55.128 Functions[13737:1176107] 第三队列——————:i=12017-07-03 15:17:55.128 Functions[13737:1176107] 第三队列——————:i=22017-07-03 15:17:55.128 Functions[13737:1176107] 第三队列——————:i=32017-07-03 15:17:55.129 Functions[13737:1176107] 第三队列——————:i=42017-07-03 15:17:55.129 Functions[13737:1176107] 第三队列——————:i=52017-07-03 15:17:55.129 Functions[13737:1176107] 第三队列——————:i=62017-07-03 15:17:55.129 Functions[13737:1176107] 第三队列——————:i=72017-07-03 15:17:55.129 Functions[13737:1176107] 第三队列——————:i=82017-07-03 15:17:55.129 Functions[13737:1176107] 第三队列——————:i=92017-07-03 15:17:55.129 Functions[13737:1175989] 第三队列:任务结束了可以把每一个i想象成几个线程共享数据的使用,每一次调用都是一次使用,根据打印结果可以看出,不同队列之间,不会在同一时刻同时调用打印i。

其实,可以通过信号量或者干脆点,直接用串行(异步相对于main的串行队列)来避免资源的抢夺。

#pragma mark - Dispatch Groups的使用/** *  是一个可以阻塞线程直到一个或多个任务完成的一种方式 *  在那些需要等待任务完成才能执行某个处理的时候,可以使用这个方法 */- (void)dispatchGroup {    weakSelf(self);    dispatch_queue_t concurrentQueue1;    concurrentQueue1 = dispatch_queue_create("com.example.ConcurrentQueue1", DISPATCH_QUEUE_CONCURRENT);    dispatch_queue_t concurrentQueue2;    concurrentQueue2 = dispatch_queue_create("com.example.ConcurrentQueue2", DISPATCH_QUEUE_CONCURRENT);    dispatch_queue_t concurrentQueue3;    concurrentQueue3 = dispatch_queue_create("com.example.ConcurrentQueue3", DISPATCH_QUEUE_CONCURRENT);    dispatch_group_t groupQueue = dispatch_group_create();    dispatch_group_async(groupQueue, concurrentQueue1, ^{        for (NSInteger i = 0; i < 1000; i++) {            NSLog(@"第一队列:i=%ld",i);        }        dispatch_async(dispatch_get_main_queue(), ^{            /// 使用完数据,发送一个信号            [weakSelf printNum:@"第一队列任务结束了"];        });    });    dispatch_group_async(groupQueue, concurrentQueue2, ^{        for (NSInteger i = 0; i < 1000; i++) {            NSLog(@"第二队列_____:i=%ld",i);        }        dispatch_async(dispatch_get_main_queue(), ^{            /// 使用完数据,发送一个信号            [weakSelf printNum:@"第二队列:任务结束了"];        });    });    dispatch_group_async(groupQueue, concurrentQueue3, ^{        for (NSInteger i = 0; i < 1000; i++) {            NSLog(@"第三队列——————:i=%ld",i);        }        dispatch_async(dispatch_get_main_queue(), ^{            /// 使用完数据,发送一个信号            [weakSelf printNum:@"第三队列:任务结束了"];        });    });    dispatch_group_async(groupQueue, dispatch_get_global_queue(0, 0), ^{//        dispatch_async(dispatch_get_global_queue(0, 0), ^{//            for (NSInteger i = 0; i < 100000; i++) {//                NSLog(@"全局队列——————:i=%ld",i);//            }//        });        /// 这种情况下,dispatch_group_notify会一直等待它完成,但是如果是上面那种情况,内部再开一个异步并行的,dispatch_group_notify就不会等待,或者说捕捉不到该等待,所以对于网络请求(基本都是异步请求的),是不可以同时放几个请求在group下面,用来等待全部完成的时候refreshUI的        for (NSInteger i = 0; i < 100000; i++) {            NSLog(@"全局队列——————:i=%ld",i);        }    });    dispatch_group_notify(groupQueue, dispatch_get_main_queue(), ^{        NSLog(@"三个任务都完成了");    });}

对于多个任务,可以开启多个异步并行队列,同时开启多个任务(如果CPU支持的话),用dispatch_group_notify来等待最终的结果。但是不可以在单个队列内部再开启一个异步并行队列(比如UI的刷新同时依赖几个请求的完成),dispatch_group_async是无法捕捉到这样的结束的。

以上就是我对GCD的基本了解以及GCD的常用API,如有错误,希望不吝指正,谢谢!
原创粉丝点击