程序员iOSiOS程序猿

iOS GCD全析(二)

2018-11-10  本文已影响13人  ChinaChong

本文摘录自《Objective-C高级编程》一书,附加一些自己的理解,作为对GCD的总结。



此篇主要包含以下几个方面:


dispatch_async(异步) / dispatch_sync(同步)

添加方式 并发队列 串行队列 主队列
同步(sync) 没有开启新线程
串行执行任务
没有开启新线程
串行执行任务
没有开启新线程
串行执行任务
异步(async) 开启新线程
并发执行任务
开启新线程
串行执行任务
没有开启新线程
串行执行任务

注:所谓开启新线程是指任务在主线程之外的线程中执行,即产生了主线程以外的新线程

dispatch_async函数的“async”意味着“非同步”(asynchronous),就是将指定的Block“非同步”地追加到指定的Dispatch Queue中。dispatch_async函数不做任何等待。

dispatch_sync函数意味着“同步”(synchronous),也就是将指定的Block“同步”追加到指定的Dispatch Queue中。在追加Block结束之前,dispatch_sync函数会一直等待。一旦调用dispatch_sync函数,那么在指定的处理执行结束之前,该函数不会返回。

下面我们用实际案例逐一体验:

同步函数+并发队列


- (void)viewDidLoad {
    [super viewDidLoad];
    [self sync_concurrent];
}

- (void)sync_concurrent {
    
    dispatch_queue_t queue = dispatch_queue_create("com.example.sync_concurrent", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_sync(queue, ^{
        NSLog(@"任务1 当前线程:%@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"任务2 当前线程:%@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"任务3 当前线程:%@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"任务4 当前线程:%@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"任务5 当前线程:%@",[NSThread currentThread]);
    });
}

控制台:

总结:

同步函数+串行队列


- (void)viewDidLoad {
    [super viewDidLoad];
    [self sync_serial];
}

- (void)sync_serial {
    dispatch_queue_t queue = dispatch_queue_create("com.example.sync_serial", DISPATCH_QUEUE_SERIAL);
    
    dispatch_sync(queue, ^{
        NSLog(@"任务1 当前线程:%@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"任务2 当前线程:%@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"任务3 当前线程:%@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"任务4 当前线程:%@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"任务5 当前线程:%@",[NSThread currentThread]);
    });
}

控制台:

总结:

同步函数+主队列


- (void)viewDidLoad {
    [super viewDidLoad];
    [self sync_mainQueue];
}

- (void)sync_mainQueue {
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"任务1 当前线程:%@",[NSThread currentThread]);
    });
    
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"任务2 当前线程:%@",[NSThread currentThread]);
    });
    
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"任务3 当前线程:%@",[NSThread currentThread]);
    });
    
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"任务4 当前线程:%@",[NSThread currentThread]);
    });
    
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"任务5 当前线程:%@",[NSThread currentThread]);
    });
}

😎
😎😎
😎😎😎 此处线程死锁,各位客官可以移步iOS中的线程死锁,看个究竟。
😎😎
😎

异步函数+并发队列


- (void)viewDidLoad {
    [super viewDidLoad];
    [self async_concurrent];
}

- (void)async_concurrent {
    dispatch_queue_t queue = dispatch_queue_create("com.example.async_concurrent", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"任务1 当前线程:%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"任务2 当前线程:%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"任务3 当前线程:%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"任务4 当前线程:%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"任务5 当前线程:%@",[NSThread currentThread]);
    });
}

控制台:

总结:

异步函数+串行队列


- (void)viewDidLoad {
    [super viewDidLoad];
    [self async_serial];
}

- (void)async_serial {
    dispatch_queue_t queue = dispatch_queue_create("com.example.async_serial", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue, ^{
        NSLog(@"任务1 当前线程:%@",[NSThread currentThread]);
        sleep(1);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"任务2 当前线程:%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"任务3 当前线程:%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"任务4 当前线程:%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"任务5 当前线程:%@",[NSThread currentThread]);
        sleep(1);
    });
}

控制台:

总结:

异步函数+主队列


- (void)viewDidLoad {
    [super viewDidLoad];
    [self async_mainQueue];
}

- (void)async_mainQueue {
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"任务1 当前线程:%@",[NSThread currentThread]);
    });
    
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"任务2 当前线程:%@",[NSThread currentThread]);
    });
    
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"任务3 当前线程:%@",[NSThread currentThread]);
    });
    
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"任务4 当前线程:%@",[NSThread currentThread]);
    });
    
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"任务5 当前线程:%@",[NSThread currentThread]);
    });
}

控制台:

总结:


dispatch_after(延迟执行)

经常会有这样的情况:想在3秒后执行处理。可能不仅限于3秒,总之,这种想在指定时间后执行处理的情况,可使用dispatch_after函数来实现。

在3秒后将指定的Block 追加到Main Dispatch Queue中的源代码如下:


dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    NSLog(@"wait at least three seconds.");
});
/*
* 或者使用下面这种方式
*/

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
    NSLog(@"wait at least three seconds.");
});

需要注意的是,dispatch_after函数并不是在指定时间后执行处理,而只是在指定时间后追加处理到Dispatch Queue。此源代码与在3秒后用dispatch_async函数追加Block到Main Dispatch Queue的相同。

因为Main Dispatch Queue在主线程的RunLoop中执行,所以在比如每隔1/60秒执行的RunLoop中,Block最快在3秒后执行,最慢在3秒+1/60秒后执行,并且在Main Dispatch Queue有大量处理追加或主线程的处理本身有延迟时,这个时间会更长。

虽然在有严格时间的要求下使用时会出现问题,但在想大致延迟执行处理时,该函数是非常有效的。

另外,第二个参数指定要追加处理的Dispatch Queue,第三个参数指定记述要执行处理的Block。

第一个参数是指定时间用的dispatch_time_t类型的值。该值使用dispatch_time函数或dispatch_walltime函数作成。

dispatch_time函数能够获取从第一个参数dispatch_time_t类型值中指定的时间开始,到第二个参数指定的毫微秒单位时间后的时间。第一个参数经常使用的值是之前源代码中出现的DISPATCH_TIME_NOW。这表示现在的时间。即以下源代码可得到表示从现在开始1秒后的dispatch_time_t类型的值。


dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);

dispatch_walltime函数由POSIX中使用的struct timespec 类型的时间得到dispatch_time_t类型的值。dispatch_time 函数通常用于计算相对时间,而dispatch_walltme函数用于计算绝对时间。例如在dispatch_after函数中想指定2011年11月1日11时11分1秒这一绝对时间的情况,这可作为粗略的闹钟功能使用。

struct timespec类型的时间可以很轻松地通过NSDate类对象作成。


dispatch_apply(重复执行)

dispatch_apply函数是dispatch_sync函数和Dispatch Group的关联API。该函数按指定的次数将指定的Block追加到指定的Dispatch Queue中,并等待全部处理执行结束。

举例:


dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_apply(10, queue, ^(size_t index) {
    NSLog(@"%zu\t当前线程:%@",index,[NSThread currentThread]);
});

NSLog(@"done");



控制台:

说明:

因为在Global Dispatch Queue中执行处理,所以各个处理的执行时间不定。但是输出结果中最后的done必定在最后的位置上。这是因为dispatch_apply函数会等待全部处理执行结束。

第一个参数为重复次数,第二个参数为追加对象的Dispatch Queue,第三个参数为追加的处理。与其它的函数不同,第三个参数的Block为带有参数的Block。这是为了按第一个参数重复追加Block并区分各个Block而使用。

总结:

举一个实际例子:

要对NSArray类对象的所有元素执行处理时,不必一个一个编写for循环,参考以下源代码


NSArray *arr = @[@"一",@"二",@"三",@"四",@"五",@"六",@"七",@"八",@"九",@"十"];

dispatch_apply(arr.count, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) {
    NSLog(@"%@\t当前线程:%@",[arr objectAtIndex:index],[NSThread currentThread]);
});

控制台:

这样可简单地在Global Dispatch Queue中对所有元素执行Block。

另外,由于dispatch_apply函数也与dispatch_sync函数相同,会等待处理执行结束,因此推荐在dispatch_async函数中非同步地执行dispatch_apply函数。


NSArray *array = [NSArray array];

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

/*
 *  在Global Dispatch Queue中非同步执行
 */

dispatch_async(queue, ^{
    /*
     *  Global Dispatch Queue
     *  等待dispatch_apply函数中全部处理执行结束
     */
    
    dispatch_apply([array count], queue, ^(size_t index) {
        /*
         *  并列处理包含在NSArray对象的全部对象
         */
        
        NSLog(@"%zu:%@", index, [array objectAtIndex:index]);
    });
    
    /*
     *  dispatch_apply函数中的处理全部结束
     */
    
    /*
     *  在Main Dispatch Queue中非同步执行
     */
    dispatch_async(dispatch_get_main_queue(), ^{
        /*
         *  在Main Dispatch Queue执行处理
         *  用户界面更新等
         */
        NSLog(@"done");
    });
});

上一篇下一篇

猜你喜欢

热点阅读