GCD学习总结

GCD

GCD的全称是Grand Central Dispatch,字面意思就是“极好的中枢调度器”,它能通过队列和任务的形式实现多线程编程。使得编程人员不需要编写线程代码。

一、 dispatch queue

GCD的使用就是开发者只需要将想要执行的任务放入队列中就能实现相应的多线程操作。那么队列是什么呢? 就像它的名字一样,就是执行处理任务的队列。即 dispatch queue。

dispatch queue有两种类型,分别为

  • 串行队列 (serial dispatch queue)
  • 并发队列 (concurrent dispatch queue)

串行队列和并发队列的区别,放入串行队列中的任务,需要等待执行中的任务处理结束才能进行下一个任务。而放入并发队列的任务,与之相反,不需要等待当前执行中的任务处理结束就可以同时进行其它的任务。

队列的创建方式

1 通过 dispatch_queue_create 创建队列

通过系统提供的 dispatch_queue_create API可以创建dispatch queue。

// 创建串行队列
    dispatch_queue_t serialQueue = dispatch_queue_create("come.example.gcd", NULL);

// 创建并发队列
  dispatch_queue_t concurrentQueue = dispatch_queue_create("come.example.gcd.concurrent", DISPATCH_QUEUE_CONCURRENT);    

dispatch_queue_create 有2个参数,第一个参数是用来指定dispatch queue的名称,一般用程序的bundle id 来命名。第二个参数的作用是用来指定队列的类型的作用。当指定为null时,是创建串行队列,当指定为DISPATCH_QUEUE_CONCURRENT 时为并发队列。

2 利用系统自带的队列

在不想自己去创建队列的情况下,还可以通过系统提供的dispatch queue。分别为main dispatch queue 和 global dispatch queue。

main dispatch queue 就和它的名字一样,是在主线程执行操作的队列。因为主线程只有一个,所以它必然是串行队列。而global dispatch queue 则是所有线程都能操作的并发队列。

// 主队列
 dispatch_queue_t mainQueue = dispatch_get_main_queue();

// 全局队列
 dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

对于全局对列 global dispatch queue的创建函数它有2个参数,第一个参数是指定队列的执行优先级,而第二个参数 官方的介绍是目前还没有用到,默认传0就可以了。

global queue的执行优先级有:

  • DISPATCH_QUEUE_PRIORITY_HIGH 2

  • DISPATCH_QUEUE_PRIORITY_DEFAULT 0

  • DISPATCH_QUEUE_PRIORITY_LOW (-2)

  • DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN

dispatch_sync和dispatch_async

dispatch_sync和dispatch_async 分别为同步操作函数和异步操作函数。它们都有2个参数,分别为执行的操作和操作添加到的队列。同步函数是必须要队列里的操作执行完成,不然就会一直等待。而异步函数则不需要做任何等待。

这里将列举不同的队列执行sync和async函数时的情况:

1 在串行队列 同步执行任务 (这里用 sleepForTimeInterval 来模拟耗时操作,下同)

//同步 串行队列
- (void)test2 {
    dispatch_queue_t serialQueue = dispatch_queue_create("come.example.gcd", NULL);
    NSLog(@"begin");
    dispatch_sync(serialQueue, ^{
        NSLog(@"task1 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_sync(serialQueue, ^{
        NSLog(@"task2 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_sync(serialQueue, ^{
        NSLog(@"task3 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    NSLog(@"end");
}

执行结果:

2017-01-14 23:41:59.772 GCD_test[11960:1198750] begin
2017-01-14 23:41:59.772 GCD_test[11960:1198750] task1 <NSThread: 0x7fd46ac05270>{number = 1, name = main}
2017-01-14 23:42:01.774 GCD_test[11960:1198750] task2 <NSThread: 0x7fd46ac05270>{number = 1, name = main}
2017-01-14 23:42:03.775 GCD_test[11960:1198750] task3 <NSThread: 0x7fd46ac05270>{number = 1, name = main}
2017-01-14 23:42:05.775 GCD_test[11960:1198750] end

结论:加入到队列中的任务按添加顺序执行,所有的操作执行完后才打印end.没有创建新的线程。

2 在串行队列 异步执行任务

//异步 串行队列
- (void)test3 {
    dispatch_queue_t serialQueue = dispatch_queue_create("come.example.gcd", NULL);
    NSLog(@"begin");
    dispatch_async(serialQueue, ^{
        NSLog(@"task1 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_async(serialQueue, ^{
        NSLog(@"task2 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_async(serialQueue, ^{
        NSLog(@"task3 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    NSLog(@"end");
}

执行结果:

2017-01-14 23:49:14.422 GCD_test[11977:1202271] begin
2017-01-14 23:49:14.423 GCD_test[11977:1202271] end
2017-01-14 23:49:14.423 GCD_test[11977:1202430] task1 <NSThread: 0x7fdf09f240e0>{number = 2, name = (null)}
2017-01-14 23:49:16.428 GCD_test[11977:1202430] task2 <NSThread: 0x7fdf09f240e0>{number = 2, name = (null)}
2017-01-14 23:49:18.431 GCD_test[11977:1202430] task3 <NSThread: 0x7fdf09f240e0>{number = 2, name = (null)}

结论:加入到队列中的任务按顺序执行,不需要等待任务执行完成就能进行end的打印,创建了一条新的线程。

3 在并发队列 同步执行任务

//同步 并发队列
- (void)test4 {
    dispatch_queue_t concurrentQueue = dispatch_queue_create("come.example.gcd.concurrent", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"begin");
    dispatch_sync(concurrentQueue, ^{
        NSLog(@"task1 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_sync(concurrentQueue, ^{
        NSLog(@"task2 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_sync(concurrentQueue, ^{
        NSLog(@"task3 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    NSLog(@"end");
}

执行结果:

2017-01-14 23:57:36.889 GCD_test[12010:1206188] begin
2017-01-14 23:57:36.889 GCD_test[12010:1206188] task1 <NSThread: 0x7fb5bb701ab0>{number = 1, name = main}
2017-01-14 23:57:38.890 GCD_test[12010:1206188] task2 <NSThread: 0x7fb5bb701ab0>{number = 1, name = main}
2017-01-14 23:57:40.890 GCD_test[12010:1206188] task3 <NSThread: 0x7fb5bb701ab0>{number = 1, name = main}
2017-01-14 23:57:42.892 GCD_test[12010:1206188] end

结论: 加入到队列中的任务按顺序执行,所有的操作执行完后才打印end,没有创建新的线程。

4 在并发队列 异步执行任务

//异步 并发队列
- (void)test5 {
    dispatch_queue_t concurrentQueue = dispatch_queue_create("come.example.gcd.concurrent", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"begin");
    dispatch_async(concurrentQueue, ^{
        NSLog(@"task1 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"task2 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"task3 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    NSLog(@"end");
}

执行结果:

2017-01-15 00:02:25.811 GCD_test[12027:1208849] begin
2017-01-15 00:02:25.812 GCD_test[12027:1208849] end
2017-01-15 00:02:25.812 GCD_test[12027:1208900] task2 <NSThread: 0x7fc771d1cac0>{number = 3, name = (null)}
2017-01-15 00:02:25.812 GCD_test[12027:1208909] task1 <NSThread: 0x7fc771d2be60>{number = 2, name = (null)}
2017-01-15 00:02:25.812 GCD_test[12027:1208914] task3 <NSThread: 0x7fc771f04870>{number = 4, name = (null)}

结论:加入到队列中的任务不按顺序执行,不需要等待任务执行完成就能进行end的打印,创建了多条新的线程。

5 在主队列 同步执行任务 (主队列其实算一种特殊的串行队列,它的所有任务都在主线程执行,所以单独拿出来做讨论)

//同步 主队列
- (void)test6 {
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    NSLog(@"begin");
    dispatch_sync(mainQueue, ^{
        NSLog(@"task1 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_sync(mainQueue, ^{
        NSLog(@"task2 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_sync(mainQueue, ^{
        NSLog(@"task3 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    NSLog(@"end");
}

执行结果:

2017-01-15 00:07:03.129 GCD_test[12045:1210976] begin

结论:这里比较特殊 只打印了函数开始的begin。只是因为主队列中的新添加的操作需要等待该函数的执行完成,而由于是同步执行,该函数又需要等待操作执行完才能继续往下执行。所以这样互相等待就形成了一种死锁的情况。

6 在主队列 异步执行任务

//异步 主队列
- (void)test7 {
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    NSLog(@"begin");
    dispatch_async(mainQueue, ^{
        NSLog(@"task1 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_async(mainQueue, ^{
        NSLog(@"task2 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_async(mainQueue, ^{
        NSLog(@"task3 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    NSLog(@"end");
}

执行结果:

2017-01-15 00:11:03.326 GCD_test[12058:1213150] begin
2017-01-15 00:11:03.326 GCD_test[12058:1213150] end
2017-01-15 00:11:03.330 GCD_test[12058:1213150] task1 <NSThread: 0x7fb92be00720>{number = 1, name = main}
2017-01-15 00:11:05.332 GCD_test[12058:1213150] task2 <NSThread: 0x7fb92be00720>{number = 1, name = main}
2017-01-15 00:11:07.333 GCD_test[12058:1213150] task3 <NSThread: 0x7fb92be00720>{number = 1, name = main}

结论:加入到队列中的任务按顺序执行,不需要等待任务执行完成就能进行end的打印,在主线程执行。

出现死锁的情况

在上面的例子中,当在主线程中将主队列加入到sync函数中时,会产生死锁的情况。初次之外还有别的情况会出现死锁的情况。如下:

//死锁的情况
- (void)test8 {
    dispatch_queue_t serialQueue = dispatch_queue_create("come.example.gcd", NULL);
    NSLog(@"begin");
    dispatch_async(serialQueue, ^{
        NSLog(@"task1 %@",[NSThread currentThread]);
        dispatch_sync(serialQueue, ^{
            NSLog(@"task2 sync");
        });
    });
    NSLog(@"end");
}

执行结果:

2017-01-15 18:27:28.518 GCD_test[12233:1223867] begin
2017-01-15 18:27:28.518 GCD_test[12233:1223867] end
2017-01-15 18:27:28.518 GCD_test[12233:1223983] task1 <NSThread: 0x7ff503d037f0>{number = 2, name = (null)}

结论: task1的任务能正常执行,但是task2的任务由于是sync函数添加的,它需要等待task2的任务,该函数才能再往下执行,而该函数又是serialQueue,其中的任务需要一个接一个执行。task1需要task2执行完,task2需要等待task1执行完,所以就产生了死锁的情况。

二、 GCD的常见API

dispatch_after

dispatch_after是能让任务在指定的时间之后才开始执行。等价于在指定的时间之后,再将任务添加到队列中去。

dispatch_after(dispatch_time_t when,
    dispatch_queue_t queue,
    dispatch_block_t block);

三个参数分别为

1 when:指定的时间类型。

参数when是一种叫 dispatch_time_t 的类型。它可以通过dispatch_time()函数创建

//代表从现在开始3秒后的时间
 dispatch_time_t time= dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);
 

2 queue: 任务添加到的队列

3 block: 将要执行的任务

调用dispatch_after如下:

- (void)test9 {
    NSLog(@"begin");
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_time_t time= dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);
    dispatch_after(time, mainQueue, ^{
        NSLog(@"task");
    });
    NSLog(@"end");
}

执行结果:

2017-01-15 18:49:07.876 GCD_test[12257:1230032] begin
2017-01-15 18:49:07.877 GCD_test[12257:1230032] end
2017-01-15 18:49:11.159 GCD_test[12257:1230032] task

结论: dispatch_after函数不需要等待block执行完成,类似与异步函数。在指定的时候才开始执行添加的任务。

dispatch_group

通常会有这样的需求,在追加到队列的任务全部结束之后,要进行某些操作。如果是在串行队列中的话,只要在最后再向队列中添加想要进行的操作就行了。但是要是并发队列或者多个不同的队列时,实现起来就比较复杂了。这时就可以借助 dispatch_group 来实现了。如下所示: 将3个任务添加到 并发队列 异步执行。在任务执行完成后打印done。

//dispatch_group
- (void)test10 {
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t concurrentQueue = dispatch_queue_create("come.example.gcd.concurrent", DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    NSLog(@"begin");
    dispatch_group_async(group, concurrentQueue, ^{
        NSLog(@"task1 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_group_async(group, concurrentQueue, ^{
        NSLog(@"task2 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_group_async(group, concurrentQueue, ^{
        NSLog(@"task3 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_group_notify(group, mainQueue, ^{
        NSLog(@"done");
    });
    NSLog(@"end");
}

执行结果如下: 三个task的打印顺序不一定,done一定是在其它task执行完成后打印。end的打印不需要等待 done 执行完成。

2017-01-15 21:05:13.576 GCD_test[12306:1246083] begin
2017-01-15 21:05:13.577 GCD_test[12306:1246083] end
2017-01-15 21:05:13.577 GCD_test[12306:1246147] task2 <NSThread: 0x7ffe9370e750>{number = 2, name = (null)}
2017-01-15 21:05:13.577 GCD_test[12306:1246153] task1 <NSThread: 0x7ffe93410820>{number = 3, name = (null)}
2017-01-15 21:05:13.577 GCD_test[12306:1246157] task3 <NSThread: 0x7ffe934176b0>{number = 4, name = (null)}
2017-01-15 21:05:15.610 GCD_test[12306:1246083] 所有任务结束

结论: dispatch_group_async 与 dispatch_async类似,无需等待block完成就能往下执行。区别是将 queue和block 添加到了 group中。dispatch_group_notify能监听到group内的任务全部执行完成。而且不需要等待dispatch_group_notify 内的block执行完成就能继续往下执行。

除了用 dispatch_group_notify 函数监听任务执行完成外,还可以通dispatch_group_wait来监听任务是否执行结束。如下:

//dispatch_group_wait
- (void)test11 {
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t concurrentQueue = dispatch_queue_create("come.example.gcd.concurrent", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"begin");
    dispatch_group_async(group, concurrentQueue, ^{
        NSLog(@"task1 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_group_async(group, concurrentQueue, ^{
        NSLog(@"task2 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_group_async(group, concurrentQueue, ^{
        NSLog(@"task3 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    NSLog(@"done");
    NSLog(@"end");
}

执行结果: 三个task的打印顺序不一定,done一定是在其它task执行完成后打印。end的打印也是在done之后

2017-01-15 21:20:34.275 GCD_test[12326:1250587] begin
2017-01-15 21:20:34.276 GCD_test[12326:1250654] task3 <NSThread: 0x7fed1be01f30>{number = 3, name = (null)}
2017-01-15 21:20:34.276 GCD_test[12326:1250640] task2 <NSThread: 0x7fed1bc3d7c0>{number = 2, name = (null)}
2017-01-15 21:20:34.276 GCD_test[12326:1250650] task1 <NSThread: 0x7fed1bf04580>{number = 4, name = (null)}
2017-01-15 21:20:36.279 GCD_test[12326:1250587] done
2017-01-15 21:20:36.279 GCD_test[12326:1250587] end

结论: dispatch_group_wait函数会阻塞住当前线程,指到在指定的时间内group内的任务执行完成,才能继续往下走。在这个实例中 dispatch_group_wait 的第二个参数等待时间是 DISPATCH_TIME_FOREVER。意味着永久等待直到任务全部执行结束为止。而且 dispatch_group_wait 有一个 long 类型的返回值。如果将该参数改为DISPATCH_TIME_NOW 即不等待又会是什么样的结果。

- (void)test12 {
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t concurrentQueue = dispatch_queue_create("come.example.gcd.concurrent", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"begin");
    dispatch_group_async(group, concurrentQueue, ^{
        NSLog(@"task1 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_group_async(group, concurrentQueue, ^{
        NSLog(@"task2 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_group_async(group, concurrentQueue, ^{
        NSLog(@"task3 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    long result = dispatch_group_wait(group, DISPATCH_TIME_NOW);
    NSLog(@"%ld",result);
    NSLog(@"done");
    NSLog(@"end");
}

执行结果:

2017-01-15 21:28:55.279 GCD_test[12339:1253834] begin
2017-01-15 21:28:55.279 GCD_test[12339:1253834] 49
2017-01-15 21:28:55.279 GCD_test[12339:1253834] done
2017-01-15 21:28:55.280 GCD_test[12339:1253834] end
2017-01-15 21:28:55.279 GCD_test[12339:1253907] task1 <NSThread: 0x7fb6d9e1aac0>{number = 2, name = (null)}
2017-01-15 21:28:55.279 GCD_test[12339:1253918] task3 <NSThread: 0x7fb6d9f03950>{number = 4, name = (null)}
2017-01-15 21:28:55.280 GCD_test[12339:1253895] task2 <NSThread: 0x7fb6d9c14b00>{number = 3, name = (null)}

结论: 如果在等待时间内group内的操作全部执行完成了,则 dispatch_group_wait 的返回值为0.

dispatch_barrier_async

在访问数据库或者文件时,一般要劲量避免出现数据错乱的问题。通常如果只进行读的操作时,可以多条线程同时执行,但在进行写的操作时不能有其它的读的操作和写的操作,不然就会出现数据错乱的问题。也就是说在进行写的操作时 必须是在其它操作结束的时候。其实通过dispatch group可以实现。但是通过 dispatch_barrier_async 能更简单的实现。

// dispatch_barrier_async
- (void)test13 {
    dispatch_queue_t concurrentQueue = dispatch_queue_create("come.example.gcd.concurrent", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"begin");
    dispatch_async(concurrentQueue, ^{
        NSLog(@"read1 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"read2 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"read3 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_barrier_async(concurrentQueue, ^{
        NSLog(@"write %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"read4 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"read5 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    dispatch_async(concurrentQueue, ^{
        NSLog(@"read6 %@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:2];
    });
    NSLog(@"end");
}

执行结果:

2017-01-15 22:01:25.858 GCD_test[12380:1264205] begin
2017-01-15 22:01:25.858 GCD_test[12380:1264205] end
2017-01-15 22:01:25.859 GCD_test[12380:1264265] read1 <NSThread: 0x7f9672605620>{number = 2, name = (null)}
2017-01-15 22:01:25.859 GCD_test[12380:1264270] read3 <NSThread: 0x7f9672603c60>{number = 4, name = (null)}
2017-01-15 22:01:25.859 GCD_test[12380:1264257] read2 <NSThread: 0x7f9672532050>{number = 3, name = (null)}
2017-01-15 22:01:27.865 GCD_test[12380:1264270] write <NSThread: 0x7f9672603c60>{number = 4, name = (null)}
2017-01-15 22:01:29.869 GCD_test[12380:1264270] read4 <NSThread: 0x7f9672603c60>{number = 4, name = (null)}
2017-01-15 22:01:29.869 GCD_test[12380:1264257] read5 <NSThread: 0x7f9672532050>{number = 3, name = (null)}
2017-01-15 22:01:29.869 GCD_test[12380:1264265] read6 <NSThread: 0x7f9672605620>{number = 2, name = (null)}

结论: 写操作write 一定在 前3个read操作完成的时候,而后面3个read操作一定在 写操作之后。

就像有 dispatch_barrier_async 函数,必然会有dispatch_barrier_sync函数。该函数就如dispatch_sync函数一样 等待 dispatch_barrier_sync的block执行完成后才会继续往下执行。

dispatch_semaphore

当多线程进行数据的更新处理时,会容易产生数据不一致的错误。虽然可以借助dispatch_barrier_async来处理,但还有一种更细的粒度的处理函数dispatch_semaphore。比如以下的多线程更新数据操作:

- (void)test14 {
    NSMutableArray *tempArray = [NSMutableArray array];
    dispatch_queue_t concurrentQueue = dispatch_queue_create("come.example.gcd.concurrent", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"begin");
    for (int i = 0; i < 10; i++) {
        dispatch_async(concurrentQueue, ^{
            NSLog(@"obj %d",i);
            [tempArray addObject:@(i)];
        });
    }
    NSLog(@"end");
}

由于该函数是多线程异步更新 数组的操作,很容易错误导致程序崩溃。

dispatch_semaphore是一种持有计数的信号。这种信号类似于一种指示灯,当在某种数值时,允许操作。当在其它数值时,则不允许操作。dispatch_semaphore_t 便是这种计数信号,它可以通过dispatch_semaphore_create()函数创建,其中的参数为创建后 dispatch_semaphore_t 的计数值是多少。与之配合使用的2个函数是dispatch_semaphore_wait 和 dispatch_semaphore_signal。

1 dispatch_semaphore_wait()函数有2个参数,第一个是 dispatch_semaphore_t 类型的计数值,第二个是 dispatch_time_t 的等待时间。当 dispatch_semaphore_t 的值大于等于1时才能继续往下执行。然后 dispatch_semaphore_t 的值减 1.

2 dispatch_semaphore_signal()函数的参数 为dispatch_semaphore_t 类型的计数值,它能使计数值加1.

之前的函数利用dispatch_semaphore后如下:

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    NSMutableArray *tempArray = [NSMutableArray array];
    dispatch_queue_t concurrentQueue = dispatch_queue_create("come.example.gcd.concurrent", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"begin");
    for (int i = 0; i < 10; i++) {
        dispatch_async(concurrentQueue, ^{
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            NSLog(@"obj %d",i);
            [tempArray addObject:@(i)];
            dispatch_semaphore_signal(semaphore);
        });
    }
    NSLog(@"end");
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容

  • 章节目录 什么是GCD? 如何在多条路径中执行CPU命令列? 即使多线程存在很多问题(如数据竞争、死锁、线程过多消...
    DrunkenMouse阅读 840评论 1 13
  • 背景 担心了两周的我终于轮到去医院做胃镜检查了!去的时候我都想好了最坏的可能(胃癌),之前在网上查的症状都很相似。...
    Dely阅读 9,217评论 21 42
  • 谈到iOS多线程,一般都会谈到四种方式:pthread、NSThread、GCD和NSOperation。其中,苹...
    攻城狮GG阅读 260评论 0 3
  • 以前总在说一些有的没的,关于青春、关于失去,那些已不属于现在的你我,你总是说过去未来都可以,只有今天不行,而我只有...
    纯_美妞阅读 258评论 0 0
  • 都说产品是亲妈,运营是奶妈/养母,一个生一个养,但都把孩子当作自己的,自然免不了常有撕逼发生。 在公司经常会出现这...
    奔跑的大橘子阅读 624评论 3 25