iOS多线程:GCD

1.GCD的概念

GCD为Grand Central Dispatch的缩写。
它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并行任务。在Mac OS X 10.6雪豹中首次推出,也可在IOS 4及以上版本使用。

2.GCD的队列和任务

GCD包括两个核心概念:队列任务

2.1队列

队列是一种特殊的线性表,遵循FIFO(先进先出)原则,新添加的任务在队尾,执行任务的时候从队首开始读取并执行。队列分为串行队列并行队列,区别主要是:队列中任务执行的顺序不同,开启的线程数也不同。

串行队列

每个时间段只有一个任务在执行,只开启一个线程,一个任务执行完以后再执行下一个任务。

并行队列

可以有多个任务在并发执行,可以开启多个线程同时处理多个任务。

2.2任务

任务就是需要执行的一组操作,在GCD是放在block中的那一段代码,任务分为同步任务异步任务,区别是:是否等待队列的任务执行结束,以及是否具备开启新线程的能力。

同步sync

同步任务会等待操作执行完毕,再执行block外的操作,只能在当前线程中执行任务。

异步async

异步任务不会等待操作执行完毕,会直接继续执行block外的操作,可以在开启新的线程执行任务。

3.GCD的具体使用

3.1创建队列

dispatch_queue_t serial_queue = dispatch_queue_create("serial_queue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t concurrent_queue = dispatch_queue_create("concurrent_queue", DISPATCH_QUEUE_CONCURRENT);

3.2队列和任务的简单组合

3.2.1串行+同步

//    串行队列+同步
    NSLog(@"主流程start--%@", [NSThread currentThread]);
    dispatch_sync(serial_queue, ^{
        NSLog(@"同步1--操作1--%@", [NSThread currentThread]);
        sleep(3);
        NSLog(@"同步1--操作2--%@", [NSThread currentThread]);
    });
    dispatch_sync(serial_queue, ^{
        NSLog(@"同步2--操作1--%@", [NSThread currentThread]);
        sleep(1);
        NSLog(@"同步2--操作2--%@", [NSThread currentThread]);
    });
    dispatch_sync(serial_queue, ^{
        NSLog(@"同步3--操作1--%@", [NSThread currentThread]);
        sleep(2);
        NSLog(@"同步3--操作2--%@", [NSThread currentThread]);
    });
    NSLog(@"主流程end-----%@", [NSThread currentThread]);
2021-03-20 17:08:38.693421+0800 test1[8934:9017257] 主流程start--{number = 1, name = main}
2021-03-20 17:08:38.693566+0800 test1[8934:9017257] 同步1--操作1--{number = 1, name = main}
2021-03-20 17:08:41.694375+0800 test1[8934:9017257] 同步1--操作2--{number = 1, name = main}
2021-03-20 17:08:41.694603+0800 test1[8934:9017257] 同步2--操作1--{number = 1, name = main}
2021-03-20 17:08:42.695118+0800 test1[8934:9017257] 同步2--操作2--{number = 1, name = main}
2021-03-20 17:08:42.695589+0800 test1[8934:9017257] 同步3--操作1--{number = 1, name = main}
2021-03-20 17:08:44.696285+0800 test1[8934:9017257] 同步3--操作2--{number = 1, name = main}
2021-03-20 17:08:44.696645+0800 test1[8934:9017257] 主流程end-----{number = 1, name = main}

3.2.2并行+同步

//    并行队列+同步
    NSLog(@"主流程start--%@", [NSThread currentThread]);
    dispatch_sync(concurrent_queue, ^{
        NSLog(@"同步1--操作1--%@", [NSThread currentThread]);
        sleep(3);
        NSLog(@"同步1--操作2--%@", [NSThread currentThread]);
    });
    dispatch_sync(concurrent_queue, ^{
        NSLog(@"同步2--操作1--%@", [NSThread currentThread]);
        sleep(1);
        NSLog(@"同步2--操作2--%@", [NSThread currentThread]);
    });
    dispatch_sync(concurrent_queue, ^{
        NSLog(@"同步3--操作1--%@", [NSThread currentThread]);
        sleep(2);
        NSLog(@"同步3--操作2--%@", [NSThread currentThread]);
    });
    NSLog(@"主流程end-----%@", [NSThread currentThread]);
2021-03-20 17:07:19.312244+0800 test1[8866:9015712] 主流程start--{number = 1, name = main}
2021-03-20 17:07:19.312456+0800 test1[8866:9015712] 同步1--操作1--{number = 1, name = main}
2021-03-20 17:07:22.313735+0800 test1[8866:9015712] 同步1--操作2--{number = 1, name = main}
2021-03-20 17:07:22.314070+0800 test1[8866:9015712] 同步2--操作1--{number = 1, name = main}
2021-03-20 17:07:23.315434+0800 test1[8866:9015712] 同步2--操作2--{number = 1, name = main}
2021-03-20 17:07:23.315738+0800 test1[8866:9015712] 同步3--操作1--{number = 1, name = main}
2021-03-20 17:07:25.317084+0800 test1[8866:9015712] 同步3--操作2--{number = 1, name = main}
2021-03-20 17:07:25.317458+0800 test1[8866:9015712] 主流程end-----{number = 1, name = main}

3.2.3串行+异步

//    串行队列+异步
       NSLog(@"主流恒start--%@", [NSThread currentThread]);
    dispatch_async(serial_queue, ^{
        NSLog(@"异步1--操作1--%@", [NSThread currentThread]);
        sleep(3);
        NSLog(@"异步1--操作2--%@", [NSThread currentThread]);
    });
    dispatch_async(serial_queue, ^{
        NSLog(@"异步2--操作1--%@", [NSThread currentThread]);
        sleep(1);
        NSLog(@"异步2--操作2--%@", [NSThread currentThread]);
    });
    dispatch_async(serial_queue, ^{
        NSLog(@"异步3--操作1--%@", [NSThread currentThread]);
        sleep(2);
        NSLog(@"异步3--操作2--%@", [NSThread currentThread]);
    });
    NSLog(@"主流程end-----%@", [NSThread currentThread]);
2021-03-20 16:52:39.159286+0800 test1[8200:9002593] 主流恒start--{number = 1, name = main}
2021-03-20 16:52:39.159443+0800 test1[8200:9002593] 主流程end-----{number = 1, name = main}
2021-03-20 16:52:39.159462+0800 test1[8200:9002672] 异步1--操作1--{number = 6, name = (null)}
2021-03-20 16:52:42.164330+0800 test1[8200:9002672] 异步1--操作2--{number = 6, name = (null)}
2021-03-20 16:52:42.164759+0800 test1[8200:9002672] 异步2--操作1--{number = 6, name = (null)}
2021-03-20 16:52:43.166518+0800 test1[8200:9002672] 异步2--操作2--{number = 6, name = (null)}
2021-03-20 16:52:43.166932+0800 test1[8200:9002672] 异步3--操作1--{number = 6, name = (null)}
2021-03-20 16:52:45.173185+0800 test1[8200:9002672] 异步3--操作2--{number = 6, name = (null)}
image.png

3.2.4并行+异步

NSLog(@"主流恒start--%@", [NSThread currentThread]);
    dispatch_async(concurrent_queue, ^{
        NSLog(@"异步1--操作1--%@", [NSThread currentThread]);
        sleep(3);
        NSLog(@"异步1--操作2--%@", [NSThread currentThread]);
    });
    dispatch_async(concurrent_queue, ^{
        NSLog(@"异步2--操作1--%@", [NSThread currentThread]);
        sleep(1);
        NSLog(@"异步2--操作2--%@", [NSThread currentThread]);
    });
    dispatch_async(concurrent_queue, ^{
        NSLog(@"异步3--操作1--%@", [NSThread currentThread]);
        sleep(2);
        NSLog(@"异步3--操作2--%@", [NSThread currentThread]);
    });
    NSLog(@"主流程end-----%@", [NSThread currentThread]);
2021-03-20 16:44:46.396510+0800 test1[7871:8996717] 主流恒start--{number = 1, name = main}
2021-03-20 16:44:46.396648+0800 test1[7871:8996717] 主流程end-----{number = 1, name = main}
2021-03-20 16:44:46.396687+0800 test1[7871:8996802] 异步1--操作1--{number = 3, name = (null)}
2021-03-20 16:44:46.396702+0800 test1[7871:8996806] 异步2--操作1--{number = 5, name = (null)}
2021-03-20 16:44:46.396707+0800 test1[7871:8996803] 异步3--操作1--{number = 4, name = (null)}
2021-03-20 16:44:47.398008+0800 test1[7871:8996806] 异步2--操作2--{number = 5, name = (null)}
2021-03-20 16:44:48.397575+0800 test1[7871:8996803] 异步3--操作2--{number = 4, name = (null)}
2021-03-20 16:44:49.398078+0800 test1[7871:8996802] 异步1--操作2--{number = 3, name = (null)}
image.png

结论

1.对于同步执行的任务,不管是放在并行队列还是串行队列中,都是按顺序执行,且当上一个任务完成以后才执行下一个任务。
2.异步执行的任务,串行队列会开启一个新线程,串行执行任务,并行队列会开启多个新线程,并行执行任务。

image.png

3.3死锁

//  串行队列
    dispatch_queue_t queue = dispatch_queue_create("cooci", DISPATCH_QUEUE_SERIAL);
    NSLog(@"1");
    // 异步函数
    dispatch_async(queue, ^{
        NSLog(@"2");
        dispatch_sync(queue, ^{
            NSLog(@"3");
        });
        NSLog(@"4");
    });
    NSLog(@"5");
image.png

结果:输出1、5、2以后产生死锁崩溃
造成死锁的原因:输出2以后,将输出3的这个同步函数(红框部分)添加到同步队列,那么如果要执行红框部分的代码,按照队列先进先出的规则,则需要先执行完排在前面的异步函数(绿框部分),但是该异步函数又需等待红框部分执行完以后,产生死锁。
结论:在串行队列中添加同步任务时,注意线程阻塞的问题,iOS主队列是串行队列,如果在主线程执行过程中向主队列中添加同步操作时也会产生死锁,另外开辟一个线程向主队列添加同步操作则不会产生死锁。

例如在viewDidLoad函数中调用serialQueueProblem会在输出1以后产生死锁崩溃。

- (void)serialQueueProblem{
    NSLog(@"1");
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"2");
    });
    NSLog(@"3");
}

如果调用方式换成这样则正常输出123

[NSThread detachNewThreadSelector:@selector(serialQueueProblem) toTarget:self withObject:nil];

3.4其他用法

3.4.1 dispatch_once

- (void)dispatchOnce{
    static dispatch_once_t onceToken;
    NSLog(@"%ld", onceToken);
    dispatch_once(&onceToken, ^{
//        只会执行一次
        NSLog(@"1%@", [NSThread currentThread]);
    });
}

调用三次的执行结果

2021-03-21 18:17:02.376201+0800 test1[67067:9754075] 0
2021-03-21 18:17:02.376363+0800 test1[67067:9754075] 1{number = 1, name = main}
2021-03-21 18:17:02.376480+0800 test1[67067:9754075] -1
2021-03-21 18:17:02.376559+0800 test1[67067:9754075] -1

常用于实现一个单例

+ (instancetype)sharedInstance {
    static XXObject *_instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[XXObject alloc] init];
    });
    return _instance;
}

3.4.2 dispatch_after

多长时间之后将任务添加到队列中,所以任务并不是精准的三秒之后再执行
第一个参数:dispatch_time_t类型,用于指定延迟时间
第二个参数:指定要追加处理的队列
第三个参数:指定要执行的任务Block

- (void)dispatchAfter{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"1%@", [NSThread currentThread]);
    });
    NSLog(@"2%@", [NSThread currentThread]);
}
2021-03-21 18:21:35.749123+0800 test1[67266:9757808] 2{number = 1, name = main}
2021-03-21 18:21:39.037124+0800 test1[67266:9757808] 1{number = 1, name = main}

3.4.3 Dispatch Source

Dispatch Source的种类:
1、DISPATCH_SOURCE_TYPE_DATA_ADD 变量增加
2、DISPATCH_SOURCE_TYPE_DATA_OR 变量 OR
3、DISPATCH_SOURCE_TYPE_MACH_SEND MACH端口发送
4、DISPATCH_SOURCE_TYPE_MACH_RECV MACH端口接收
5、DISPATCH_SOURCE_TYPE_MEMORYPRESSURE 内存压力 (注:iOS8后可用)
6、DISPATCH_SOURCE_TYPE_PROC 检测到与进程相关的事件
7、DISPATCH_SOURCE_TYPE_READ 可读取文件映像
8、DISPATCH_SOURCE_TYPE_SIGNAL 接收信号
9、DISPATCH_SOURCE_TYPE_TIMER 定时器
10、DISPATCH_SOURCE_TYPE_VNODE 文件系统有变更
11、DISPATCH_SOURCE_TYPE_WRITE 可写入文件映像

使用DISPATCH_SOURCE_TYPE_TIMER可以实现一个每三秒执行一次的定时器

- (void)dispatchSource{
    _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
    dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC, 0);
    dispatch_source_set_event_handler(_timer, ^{
        NSLog(@"1%@", [NSThread currentThread]);
    });
    NSLog(@"2%@", [NSThread currentThread]);
    dispatch_resume(_timer);
    NSLog(@"3%@", [NSThread currentThread]);
}
2021-03-21 18:39:52.466493+0800 test1[68104:9773071] 2{number = 1, name = main}
2021-03-21 18:39:52.466664+0800 test1[68104:9773071] 3{number = 1, name = main}
2021-03-21 18:39:52.476591+0800 test1[68104:9773071] 1{number = 1, name = main}
2021-03-21 18:39:55.467553+0800 test1[68104:9773071] 1{number = 1, name = main}
2021-03-21 18:39:58.467371+0800 test1[68104:9773071] 1{number = 1, name = main}
2021-03-21 18:40:01.467143+0800 test1[68104:9773071] 1{number = 1, name = main}

3.4.4 dispatch_apply

按指定的次数将指定的Block追加到指定的Dispatch Queue中,并等待全部处理执行结束,用在并行队列执行顺序无序,用在串行队列则按顺序执行

- (void)dispatchApply{
    dispatch_queue_t queue1 = dispatch_queue_create("aaa", DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t queue2 = dispatch_queue_create("aaa", DISPATCH_QUEUE_SERIAL);
    dispatch_apply(5, queue1, ^(size_t index) {
        NSLog(@"并行队列%ld%@", index, [NSThread mainThread]);
    });
    NSLog(@"并行结束-----");
    dispatch_apply(5, queue2, ^(size_t index) {
        NSLog(@"串行队列%ld%@", index, [NSThread mainThread]);
    });
    NSLog(@"串行结束-----");
}
2021-03-21 18:55:42.463961+0800 test1[68871:9787727] 并行队列3{number = 1, name = (null)}
2021-03-21 18:55:42.463966+0800 test1[68871:9787725] 并行队列1{number = 1, name = (null)}
2021-03-21 18:55:42.463966+0800 test1[68871:9787728] 并行队列2{number = 1, name = (null)}
2021-03-21 18:55:42.464026+0800 test1[68871:9787730] 并行队列4{number = 1, name = (null)}
2021-03-21 18:55:42.463961+0800 test1[68871:9787529] 并行队列0{number = 1, name = main}
2021-03-21 18:55:42.464151+0800 test1[68871:9787529] 并行结束-----
2021-03-21 18:55:42.464242+0800 test1[68871:9787529] 串行队列0{number = 1, name = main}
2021-03-21 18:55:42.464341+0800 test1[68871:9787529] 串行队列1{number = 1, name = main}
2021-03-21 18:55:42.464434+0800 test1[68871:9787529] 串行队列2{number = 1, name = main}
2021-03-21 18:55:42.464674+0800 test1[68871:9787529] 串行队列3{number = 1, name = main}
2021-03-21 18:55:42.464891+0800 test1[68871:9787529] 串行队列4{number = 1, name = main}
2021-03-21 18:55:42.465079+0800 test1[68871:9787529] 串行结束-----

3.4.5 dispatch_barrier

  • 栅栏函数之前的任务执行完以后,并且栅栏函数执行完,才执行后面的操作
  • 串行队列或者系统提供的全局并发队列,这个栅栏函数的作用等同于一个同步函数的作用
  • dispatch_barrier_sync和dispatch_barrier_async的区别是,在当前线程中的操作,是否需要等待自己执行完再执行
- (void)dispatchBarrier{
    dispatch_queue_t queue = dispatch_queue_create("bbb", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        sleep(2);
        NSLog(@"1%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        sleep(1);
        NSLog(@"2%@", [NSThread currentThread]);
    });
    dispatch_barrier_sync(queue, ^{
        sleep(1);
        NSLog(@"barrier%@", [NSThread currentThread]);
    });
    NSLog(@"barrier done");
    dispatch_async(queue, ^{
        sleep(2);
        NSLog(@"3%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        sleep(1);
        NSLog(@"4%@", [NSThread currentThread]);
    });
}
2021-03-21 19:11:09.974409+0800 test1[69598:9801166] 2{number = 6, name = (null)}
2021-03-21 19:11:10.974576+0800 test1[69598:9801168] 1{number = 4, name = (null)}
2021-03-21 19:11:11.975488+0800 test1[69598:9801093] barrier{number = 1, name = main}
2021-03-21 19:11:11.975850+0800 test1[69598:9801093] barrier done
2021-03-21 19:11:12.980667+0800 test1[69598:9801166] 4{number = 6, name = (null)}
2021-03-21 19:11:13.977532+0800 test1[69598:9801168] 3{number = 4, name = (null)}

如果把dispatch_barrier_sync换成dispatch_barrier_async

2021-03-21 19:12:25.502966+0800 test1[69660:9802632] barrier done
2021-03-21 19:12:26.503388+0800 test1[69660:9802717] 2{number = 6, name = (null)}
2021-03-21 19:12:27.507601+0800 test1[69660:9802728] 1{number = 3, name = (null)}
2021-03-21 19:12:28.511868+0800 test1[69660:9802728] barrier{number = 3, name = (null)}
2021-03-21 19:12:29.514776+0800 test1[69660:9802718] 4{number = 4, name = (null)}
2021-03-21 19:12:30.515791+0800 test1[69660:9802728] 3{number = 3, name = (null)}

3.5 diapatch_group

将要并发执行的多个任务合并为一组,这样调用者就可以知道这些任务何时能全部执行完

3.5.1 新建group

dispatch_group_t group = dispatch_group_create();

3.5.2 将任务添加到group

用法一:dispatch_group_async

- (void)dispatchGroup1{
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("aaa", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_async(group, queue, ^{
        sleep(2);
        NSLog(@"1%@", [NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        sleep(1);
        NSLog(@"2%@", [NSThread currentThread]);
    });
    dispatch_group_notify(group, queue, ^{
        NSLog(@"notify%@", [NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        sleep(1);
        NSLog(@"3%@", [NSThread currentThread]);
    });
}
2021-03-21 20:28:06.280832+0800 test1[72687:9846592] 2{number = 4, name = (null)}
2021-03-21 20:28:06.280833+0800 test1[72687:9846593] 3{number = 3, name = (null)}
2021-03-21 20:28:07.279869+0800 test1[72687:9846591] 1{number = 7, name = (null)}
2021-03-21 20:28:07.280195+0800 test1[72687:9846591] notify{number = 7, name = (null)}

用法二:dispatch_group_enter+dispatch_group_leave配对使用

- (void)dispatchGroup2{
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("111", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        sleep(2);
        NSLog(@"1%@", [NSThread currentThread]);
        dispatch_group_leave(group);
    });
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        sleep(1);
        NSLog(@"2%@", [NSThread currentThread]);
        dispatch_group_leave(group);
    });
    dispatch_group_notify(group, queue, ^{
        NSLog(@"notify%@", [NSThread currentThread]);
    });
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        sleep(1);
        NSLog(@"3%@", [NSThread currentThread]);
        dispatch_group_leave(group);
    });
}
2021-03-21 20:44:53.567884+0800 test1[73477:9862570] 2{number = 5, name = (null)}
2021-03-21 20:44:53.567930+0800 test1[73477:9862573] 3{number = 3, name = (null)}
2021-03-21 20:44:54.567761+0800 test1[73477:9862572] 1{number = 6, name = (null)}
2021-03-21 20:44:54.568120+0800 test1[73477:9862572] notify{number = 6, name = (null)}

3.5.3 监听group里面的任务是否都完成

方法一:dispatch_group_notify使用方法如3.5.2
方法二:dispatch_group_wait设置等待时间,在等待时间结束后,如果还没有执行完任务组,则返回。返回0代表执行成功,非0则执行失败

- (void)dispatchGroupWait{
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("aaa", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_async(group, queue, ^{
        sleep(2);
        NSLog(@"1%@", [NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        sleep(1);
        NSLog(@"2%@", [NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        sleep(1);
        NSLog(@"3%@", [NSThread currentThread]);
    });
    long ret = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)));
    NSLog(@"ret = %ld", ret);
}
2021-03-21 20:57:35.552239+0800 test1[74073:9874594] 2{number = 5, name = (null)}
2021-03-21 20:57:35.552252+0800 test1[74073:9874597] 3{number = 3, name = (null)}
2021-03-21 20:57:36.552133+0800 test1[74073:9874595] 1{number = 4, name = (null)}
2021-03-21 20:57:36.552526+0800 test1[74073:9874485] ret = 0

如果把dispatch_group_wait的等待时长减少到1秒则返回非0

3.6 信号量 dispatch_semaphore

  • dispatch_semaphore_create(long value);创建信号量,参数为设置信号量的初始值
  • dispatch_semaphore_signal(dispatch_semaphore_t dsema);发送信号量,信号量的值+1
  • dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);等待信号量,第一个为当前等待的信号量,第二个参数为超时时间。在信号量≤0的时候会一直等待,直到超时,并且会阻塞该线程,当信号量>0时会继续执行,信号量-1。

使用信号量控制最大并发数

- (void)dispatchSemaphore{
    dispatch_group_t group = dispatch_group_create();
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(3);
    dispatch_queue_t queue = dispatch_queue_create("qqq", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i < 10; i ++) {
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        dispatch_group_async(group, queue, ^{
            sleep(1);
            NSLog(@"%d%@", i, [NSThread currentThread]);
            dispatch_semaphore_signal(semaphore);
        });
    }
    dispatch_group_notify(group, queue, ^{
        NSLog(@"done");
    });
}

你可能感兴趣的:(iOS多线程:GCD)