GCD1

作者: 霸_霸霸 | 来源:发表于2019-05-10 09:24 被阅读0次

1. 创建dispatch_queue_t

需求:我们要打印10000个数字,在打印的同时显示一个红色的视图
不使用GCD

- (void)viewDidLoad {
    [super viewDidLoad];
    for (int i = 0; i < 10000; i++) {
            NSLog(@"%d",i);
        }

    UIView *view = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 100, 100)];
    [view setCenter:CGPointMake(self.view.center.x, self.view.center.y)];
    [self.view addSubview:view];
    view.backgroundColor = [UIColor redColor];
}

上面的代码,所有的操作都是在主线程的,主线程会先把10000个数字打印完再显示图片,这样肯定不是我们想要的;

使用GCD

- (void)viewDidLoad {
    [super viewDidLoad];
    
    dispatch_queue_t myQueue = dispatch_queue_create("com.gcd.myQueue1", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(myQueue, ^{
        for (int i = 0; i < 10000; i++) {
            NSLog(@"%d",i);
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"---finished---");
        });
    });
    
    UIView *view = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 100, 100)];
    [view setCenter:CGPointMake(self.view.center.x, self.view.center.y)];
    [self.view addSubview:view];
    view.backgroundColor = [UIColor redColor];
}

显示图片的同时也在打印数字,这才是我们想要的效果


2. dispatch_after

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"1s later.");
});

注意:dispatch_after并不是表示几秒后执行Block中的代码;它表示的是几秒后,将Block追加到指定的Dispatch Queue中。

我们看下面这段代码

- (void)viewDidLoad {
    [super viewDidLoad];
    //事件1
    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC));
    dispatch_after(time, dispatch_get_main_queue(), ^{
        NSLog(@"1s later.");
    });
    //事件2
    for (int i = 0; i < 20000; i++) {
        NSLog(@"%d",i);
    }
}

事件1的代码一般简化为:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"1s later.");
    });

结果会发现,1s later是在所有的数字打印完了才输出的,而不是固定1s后输出的;这也验证了上面的说法。
如果要更准确一点,可以把这两个事件分到不同的Queue中执行,互不干涉,互不等待,可以相对准确地来触发定时事件;
尽管如此,也不能精确地表示一定是1s后执行Block中代码,这涉及到Runloop

3. dispatch_group

如果我们希望Queue中的多个处理全部结束后再执行结束处理,我们有2种做法

  • 当只有一个Serial Queue时,可以把所有的处理都追加到Serial Queue中顺序执行即可;当多个处理完成后,再执行结束处理
  • 如果是一个Concurrent Queue,其中的多个处理都是乱序的,我们不知道什么时候能完成所有的处理,所以我们需要一个分隔符,用以确定除结束操作外的所有处理都结束了,我们才可以进行结束处理;dispatch_group就是一个这样的分隔符

需求:数组array里有30000条数据,不要求数字顺序,全部打印出来即可,打印完成后,输出Read finished.

  1. Serial Queue
dispatch_queue_t serialQueue = dispatch_queue_create("com.gcd.serialQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_async(serialQueue, ^{
        for (int i = 0; i < 10000; i++) {
            NSLog(@"%@", array[i]);
        }
    });
    dispatch_async(serialQueue, ^{
        for (int i = 10000; i < 20000; i++) {
            NSLog(@"%@", array[i]);
        }
    });
    dispatch_async(serialQueue, ^{
        for (int i = 20000; i < 30000; i++) {
            NSLog(@"%@", array[i]);
        }
    });
    dispatch_async(serialQueue, ^{
        NSLog(@"Read finished.");
    });

结果


serial.png
  1. Concurrent Queue
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.gcd.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, concurrentQueue, ^{
        for (int i = 0; i < 10000; i++) {
            NSLog(@"%@",array[i]);
        }
    });
    dispatch_group_async(group, concurrentQueue, ^{
        for (int i = 10000; i < 20000; i++) {
            NSLog(@"%@",array[i]);
        }
    });
    dispatch_group_async(group, concurrentQueue, ^{
        for (int i = 20000; i < 30000; i++) {
            NSLog(@"%@",array[i]);
        }
    });
    
    dispatch_group_notify(group, concurrentQueue, ^{
        NSLog(@"Read finished.");
    });

结果


concurrent.png

或者使用dispatch_group_wait

dispatch_queue_t concurrentQueue = dispatch_queue_create("com.gcd.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, concurrentQueue, ^{
        for (int i = 0; i < 10000; i++) {
            NSLog(@"%@",array[i]);
        }
    });
    dispatch_group_async(group, concurrentQueue, ^{
        for (int i = 10000; i < 20000; i++) {
            NSLog(@"%@",array[i]);
        }
    });
    dispatch_group_async(group, concurrentQueue, ^{
        for (int i = 20000; i < 30000; i++) {
            NSLog(@"%@",array[i]);
        }
    });

    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    NSLog(@"Read finished");

dispatch_group_wait有两个参数,第一个是指定group,第二个表示如果group里的任务没有执行完成,要等待多久?DISPATCH_TIME_FOREVER就是表示:如果group中的任务没有执行完成,就一直等待。

4. dispatch_barrier_async·

  • 对同个数据读取可以多个并行执行;
  • 对同个数据进行多个写入操作是不能并行执行的,因为我们无法确定并行执行的顺序;
  • 对同个数据进行读取和写入是不能并行执行的,这样会导致数据出错

为了避免数据错乱,且要高效地完成操作,我们可以采用dispatch_barrier_async
需求:可变数组array中有0-299共300个数据,我们需要读取最后三条数据,并且插入300-399后,再读取一次最后三条数据

  1. 如果不用dispatch_barrier_async
NSMutableArray *array = [NSMutableArray array];
    for (int i = 0; i < 300; i ++) {
        [array addObject:@(i)];
    }
    dispatch_queue_t queue = dispatch_queue_create("com.testQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"%d", [array[array.count - 1] intValue]);
    });
    dispatch_async(queue, ^{
        NSLog(@"%d", [array[array.count - 2] intValue]);
    });
    dispatch_async(queue, ^{
        NSLog(@"%d", [array[array.count - 3] intValue]);
    });

    //写入操作
    dispatch_async(queue, ^{
        NSLog(@"---------Begin write----------");
        for (int i = 300; i < 400; i ++) {
            [array addObject:@(i)];
        }
    });
    
    dispatch_async(queue, ^{
        NSLog(@"%d", [array[array.count - 1] intValue]);
    });
    dispatch_async(queue, ^{
        NSLog(@"%d", [array[array.count - 2] intValue]);
    });
    dispatch_async(queue, ^{
        NSLog(@"%d", [array[array.count - 3] intValue]);
    });

结果

2019-05-15 09:55:43.535947+0800 GCD[2571:121141] 299
2019-05-15 09:55:43.535949+0800 GCD[2571:121140] 298
2019-05-15 09:55:43.535973+0800 GCD[2571:121138] 297
2019-05-15 09:55:43.535999+0800 GCD[2571:121139] ---------Begin write----------
2019-05-15 09:55:43.536086+0800 GCD[2571:121141] 299
2019-05-15 09:55:43.536087+0800 GCD[2571:121140] 298
2019-05-15 09:55:43.536132+0800 GCD[2571:121138] 299

开始插入数据后,读取到的数据是乱序的,显然不是我们想要的效果

  1. 使用dispatch_barrier_async
NSMutableArray *array = [NSMutableArray array];
    for (int i = 0; i < 300; i ++) {
        [array addObject:@(i)];
    }
    dispatch_queue_t queue = dispatch_queue_create("com.testQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"%d", [array[array.count - 1] intValue]);
    });
    dispatch_async(queue, ^{
        NSLog(@"%d", [array[array.count - 2] intValue]);
    });
    dispatch_async(queue, ^{
        NSLog(@"%d", [array[array.count - 3] intValue]);
    });
    
    //上面的读取操作可以并行执行,但是写入操作必须要单独执行
    dispatch_barrier_async(queue, ^{
        NSLog(@"---------Begin write----------");
        for (int i = 300; i < 400; i ++) {
            [array addObject:@(i)];
        }
    });
    
    dispatch_async(queue, ^{
        NSLog(@"%d", [array[array.count - 1] intValue]);
    });
    dispatch_async(queue, ^{
        NSLog(@"%d", [array[array.count - 2] intValue]);
    });
    dispatch_async(queue, ^{
        NSLog(@"%d", [array[array.count - 3] intValue]);
    });

结果

2019-05-15 09:53:12.712380+0800 GCD[2495:115079] 299
2019-05-15 09:53:12.712393+0800 GCD[2495:115077] 298
2019-05-15 09:53:12.712408+0800 GCD[2495:115078] 297
2019-05-15 09:53:12.712537+0800 GCD[2495:115078] ---------Begin write----------
2019-05-15 09:53:12.712618+0800 GCD[2495:115078] 399
2019-05-15 09:53:12.712629+0800 GCD[2495:115079] 398
2019-05-15 09:53:12.712636+0800 GCD[2495:115077] 397

是我们想要的效果
我这里的读取都比较简单,假设需要大量的读取操作,依然可以按照这样的思路设计。

相关文章

  • GCD1

    1. 创建dispatch_queue_t 需求:我们要打印10000个数字,在打印的同时显示一个红色的视图不使用...

  • 多线程

    iOS中的几种多线程GCD1、GCD分为任务和队列,任务(同步,异步)队列(串行,并发),同步串行,同步主队列的情...

网友评论

      本文标题:GCD1

      本文链接:https://www.haomeiwen.com/subject/mhkooqtx.html