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 {number = 1, name = main}
2017-01-14 23:42:01.774 GCD_test[11960:1198750] task2 {number = 1, name = main}
2017-01-14 23:42:03.775 GCD_test[11960:1198750] task3 {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 {number = 2, name = (null)}
2017-01-14 23:49:16.428 GCD_test[11977:1202430] task2 {number = 2, name = (null)}
2017-01-14 23:49:18.431 GCD_test[11977:1202430] task3 {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 {number = 1, name = main}
2017-01-14 23:57:38.890 GCD_test[12010:1206188] task2 {number = 1, name = main}
2017-01-14 23:57:40.890 GCD_test[12010:1206188] task3 {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 {number = 3, name = (null)}
2017-01-15 00:02:25.812 GCD_test[12027:1208909] task1 {number = 2, name = (null)}
2017-01-15 00:02:25.812 GCD_test[12027:1208914] task3 {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 {number = 1, name = main}
2017-01-15 00:11:05.332 GCD_test[12058:1213150] task2 {number = 1, name = main}
2017-01-15 00:11:07.333 GCD_test[12058:1213150] task3 {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 {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 {number = 2, name = (null)}
2017-01-15 21:05:13.577 GCD_test[12306:1246153] task1 {number = 3, name = (null)}
2017-01-15 21:05:13.577 GCD_test[12306:1246157] task3 {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 {number = 3, name = (null)}
2017-01-15 21:20:34.276 GCD_test[12326:1250640] task2 {number = 2, name = (null)}
2017-01-15 21:20:34.276 GCD_test[12326:1250650] task1 {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 {number = 2, name = (null)}
2017-01-15 21:28:55.279 GCD_test[12339:1253918] task3 {number = 4, name = (null)}
2017-01-15 21:28:55.280 GCD_test[12339:1253895] task2 {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 {number = 2, name = (null)}
2017-01-15 22:01:25.859 GCD_test[12380:1264270] read3 {number = 4, name = (null)}
2017-01-15 22:01:25.859 GCD_test[12380:1264257] read2 {number = 3, name = (null)}
2017-01-15 22:01:27.865 GCD_test[12380:1264270] write {number = 4, name = (null)}
2017-01-15 22:01:29.869 GCD_test[12380:1264270] read4 {number = 4, name = (null)}
2017-01-15 22:01:29.869 GCD_test[12380:1264257] read5 {number = 3, name = (null)}
2017-01-15 22:01:29.869 GCD_test[12380:1264265] read6 {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");

你可能感兴趣的:(GCD学习总结)