首先我們要搞清楚一些概念:
主線程是在可以其他隊(duì)列的任務(wù)進(jìn)行操作的
主隊(duì)列mainqueue的任務(wù)肯定在是主線程上操作的
對(duì)于串行隊(duì)列,每創(chuàng)建一個(gè)串行隊(duì)列,系統(tǒng)就會(huì)對(duì)應(yīng)創(chuàng)建一個(gè)線程,同時(shí)這些線程都是并行執(zhí)行的,只是在串行隊(duì)列中的任務(wù)是串行執(zhí)行的。大量的創(chuàng)建串行隊(duì)列會(huì)導(dǎo)致大量消耗內(nèi)存,這是不可取的做法。串行隊(duì)列的優(yōu)勢(shì)在于他是一個(gè)線程,所以在操作一個(gè)全局?jǐn)?shù)據(jù)時(shí)候是線程安全的。當(dāng)想并行執(zhí)行而不發(fā)生數(shù)據(jù)競(jìng)爭(zhēng)時(shí)候可以用并行隊(duì)列操作
如果我們cpu是單核,能不能并行操作任務(wù):不能
如果我們cpu是雙核,能不能并行操作任務(wù):能 cpu1 處理A, cpu2 處理B
串行隊(duì)列,并行隊(duì)列:
我們想象成這樣的場(chǎng)景,銀行有一個(gè)窗口
串行隊(duì)列就是需要排隊(duì)按順序辦理,在前面的就先辦理,后面的就后辦理,不能插隊(duì)
并行隊(duì)列就是他們沒有排隊(duì),完全是靠擠的,誰(shuí)先辦理也就是隨機(jī)的
串發(fā)和并發(fā):
我們想象成這樣的場(chǎng)景,串發(fā)就是只有當(dāng)前的窗口(即當(dāng)前線程,可以是主線程也可以是子線程)可以辦理,不會(huì)再開新的窗口
并發(fā)就是會(huì)再給你開多個(gè)窗口(即多個(gè)線程)辦理。
串行和串發(fā):沒有開啟新線程,任務(wù)是逐個(gè)完成的
串行和并發(fā):開啟新線程,任務(wù)是逐個(gè)完成的
并行和串發(fā):沒有開啟新線程,任務(wù)是逐個(gè)執(zhí)行
并行和并發(fā):開啟新線程,任務(wù)是并發(fā)的
串行隊(duì)列:
dispatch_queue_t eoc_queueOneTT = dispatch_queue_create("eoc_queueOneTT", DISPATCH_QUEUE_SERIAL);
打印結(jié)果:

寬度是:0x1,因?yàn)槭谴?,所以寬度?,每次只允許一個(gè)任務(wù)進(jìn)行。
并行隊(duì)列:
dispatch_queue_t eoc_queueOneTT = dispatch_queue_create("eoc_queueOneTT", DISPATCH_QUEUE_CONCURRENT);
打印結(jié)果:

寬度是:0xffe,因?yàn)槭遣⑿?,所以寬度不?,每次允許多個(gè)任務(wù)進(jìn)行。
串行隊(duì)列(串發(fā)即同步,并發(fā)即異步)
// 串行隊(duì)列 只能開啟一個(gè)任務(wù) (串發(fā),即同步 dispatch_sync 在當(dāng)前線程操作 sync:同步)
- (void)serialQueue{
// 創(chuàng)建用戶串行隊(duì)列 serial
dispatch_queue_t queue = dispatch_queue_create("serQueue", DISPATCH_QUEUE_SERIAL);
// 異步
dispatch_async(queue, ^{
NSLog(@"1:%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2:%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3:%@", [NSThread currentThread]);
});
// 同步
dispatch_sync(queue, ^{
NSLog(@"4:%@", [NSThread currentThread]);
});
}
輸出結(jié)果:

串行隊(duì)列就后面參數(shù)是:DISPATCH_QUEUE_SERIAL的時(shí)候。
同步和異步只是兩種方法的不同,dispatch_async異步的時(shí)候,會(huì)開啟新的線程。dispatch_sync同步的時(shí)候,就在當(dāng)前線程,即主線程。而且都是按照順序執(zhí)行的。
并行隊(duì)列(串發(fā)即同步,并發(fā)即異步)
// 并行隊(duì)列 同時(shí)可以開啟多個(gè)任務(wù) (并發(fā),即異步 dispatch_async 另外再開新的線程 async:異步)
- (void)conCurrentQueue{
// 創(chuàng)建用戶串行隊(duì)列 concurrent
dispatch_queue_t queue = dispatch_queue_create("serQueue", 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_sync(queue, ^{
NSLog(@"4當(dāng)前線程:%@", [NSThread currentThread]);
});
}
輸出結(jié)果:

并行隊(duì)列就后面參數(shù)是:DISPATCH_QUEUE_CONCURRENT的時(shí)候。
同步和異步只是兩種方法的不同,dispatch_async異步的時(shí)候,會(huì)開啟新的線程。dispatch_sync同步的時(shí)候,就在當(dāng)前線程,即主線程。但是都沒有按照順序執(zhí)行。
組(用于多個(gè)任務(wù)執(zhí)行)
- (void)group_gcd{
// 1 創(chuàng)建組
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("serQueue", DISPATCH_QUEUE_CONCURRENT);
// 2向組添加任務(wù) 任務(wù)數(shù)count=3
dispatch_group_async(group, queue, ^{
NSLog(@"task one::%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"task two::%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"task three::%@", [NSThread currentThread]);
});
// 3組任務(wù)全部完成了,就通知, 然后執(zhí)行下面block里面的方法 任務(wù)數(shù)count為0
dispatch_group_notify(group, queue, ^{
NSLog(@"finish all task");
});
}
輸出結(jié)果:

不管前面是串行還是并行隊(duì)列,只有組里面的添加的任務(wù)數(shù)做完了,當(dāng)任務(wù)數(shù)為0的時(shí)候,才會(huì)走最后一個(gè)組通知的方法,執(zhí)行block里面的方法。
組的應(yīng)用
一個(gè)界面有多個(gè)請(qǐng)求的時(shí)候,可以用到組。當(dāng)?shù)诙€(gè)網(wǎng)絡(luò)請(qǐng)求里面可能用到第一個(gè)請(qǐng)求成功里面的數(shù)據(jù),就要等第一個(gè)請(qǐng)求成功過后,才能執(zhí)行第二個(gè)請(qǐng)求,類似這樣的情況。
// 一個(gè)界面執(zhí)行加載多個(gè)網(wǎng)絡(luò)請(qǐng)求,可以用到group
- (void)groupStyleTwo{
// 1 創(chuàng)建組
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("serQueue", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 3; i++) {
// 填加一個(gè)空任務(wù),讓任務(wù)數(shù)+1,當(dāng)任務(wù)完成后,再移除空任務(wù),讓任務(wù)數(shù)-1,最后任務(wù)數(shù)為0的時(shí)候,才會(huì)走通知的方法
// 或者用最下面注釋的那三行寫法,效果也是一樣的
dispatch_group_enter(group); // 任務(wù)數(shù)+1
dispatch_async(queue, ^{
[self netLoadSync:i]; // 如果在這個(gè)任務(wù)再開一個(gè)線程,那么不能保證你的需求
dispatch_group_leave(group);// 任務(wù)數(shù)-1
});
// dispatch_group_async(group, queue, ^{
// [self netLoadSync:i];
// });
}
// 3組任務(wù)全部完成了,就通知
dispatch_group_notify(group, queue, ^{
NSLog(@"finish all task");
});
}
// task 同步
- (void)netLoadSync:(int)taskCount
{
NSString *urlPath = @"http://svr.tuliu.com/center/front/app/util/updateVersions";
NSString *urlstr = [NSString stringWithFormat:@"%@?versions_id=1&system_type=1", urlPath];
NSURL *url = [NSURL URLWithString:urlstr];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
// 創(chuàng)建信號(hào)量,做標(biāo)記,當(dāng)請(qǐng)求成功并完成過后,才執(zhí)行后面的代碼
dispatch_semaphore_t sema = dispatch_semaphore_create(0); // 信號(hào)量 - 1
NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error){
// NSDictionary *infoDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
NSLog(@"完成了,taskcount:%d", taskCount);
// 完成過后,發(fā)生信號(hào)量
dispatch_semaphore_signal(sema); // 信號(hào)量 - 2
}];
[task resume];
// 阻塞代碼,達(dá)到同步的效果,直到收到信號(hào)量(收到信號(hào)量,也就是說請(qǐng)求完成了),才繼續(xù)執(zhí)行
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); // 信號(hào)量 - 3
NSLog(@"finish 代碼跑完了:%d",taskCount);
}
輸出結(jié)果:

我們?yōu)榱诉_(dá)到同步的效果,使用了信號(hào)量(代碼中關(guān)于信號(hào)量的那三行代碼),即只有請(qǐng)求成功過后,才會(huì)執(zhí)行請(qǐng)求后面的代碼,以免出現(xiàn)一些錯(cuò)誤,后面要跟上一堆判斷請(qǐng)求是否成功。
如果不加那三行信號(hào)量的代碼,輸出結(jié)果如下:

柵欄
- (void)barrier_fct{
dispatch_queue_t queue = dispatch_queue_create("serQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"分界線前:taskOne");
});
dispatch_async(queue, ^{
NSLog(@"分界線前:taskTwo");
});
dispatch_async(queue, ^{
NSLog(@"分界線前:taskThree");
});
dispatch_barrier_async(queue, ^{ // 分界線里面,queue可以看作是串行的,當(dāng)前只能執(zhí)行barrier里面的task
NSLog(@"分界線里面的任務(wù)");
});
dispatch_async(queue, ^{
NSLog(@"分界線后:taskFour");
});
dispatch_async(queue, ^{
NSLog(@"分界線后:taskFive");
});
// 可以看成三個(gè)部分,分界線前面三個(gè)是一個(gè)部分,分界線里面是第二個(gè)部分,分界線后面兩個(gè)是第三個(gè)部分。而且必須是第一部分執(zhí)行完了,才能執(zhí)行第二部分,第二部分執(zhí)行完了,才能執(zhí)行第三部分。
}
輸出結(jié)果:

柵欄的應(yīng)用
讀寫數(shù)組 mutableAry,如果開幾個(gè)線程來操作數(shù)組
1 寫數(shù)組(移除index=0對(duì)象), 2 寫數(shù)組(移除了index=0的對(duì)象) // 有問題
1 讀數(shù)組 2 讀數(shù)組 // 沒問題
1 讀數(shù)組 2寫數(shù)組
只要涉及到寫操作(要做保護(hù)),當(dāng)數(shù)組寫的時(shí)候,不允許其他線程對(duì)它有操作,不然就會(huì)有問題。
- (void)viewDidLoad {
[super viewDidLoad];
_safeAry = [NSMutableArray array];
[_safeAry addObject:@"0"];
[_safeAry addObject:@"1"];
[_safeAry addObject:@"2"];
[_safeAry addObject:@"3"];
rwQueue = dispatch_queue_create("serQueue", DISPATCH_QUEUE_CONCURRENT);
}
- (void)testRWAry{
dispatch_queue_t queue = dispatch_queue_create("testRWAry", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 20; i++) {
// 讀
dispatch_async(queue, ^{
NSLog(@"%d::%@", i, [self indexTo:i]);
});
// 寫
dispatch_async(queue, ^{
[self addObject:[NSString stringWithFormat:@"%d", i+4]];
});
}
}
// 寫 保證只有一個(gè)在操作,所以是把這個(gè)操作放在柵欄里面,而不是它的前面或者后面,因?yàn)楫?dāng)執(zhí)行柵欄里面操作的時(shí)候,就只有它在執(zhí)行(避免了同時(shí)多個(gè)寫操作導(dǎo)致的問題)
- (void)addObject:(NSString*)object{
dispatch_barrier_async(rwQueue, ^{
if (object != nil) {
[_safeAry addObject:object];
}
});
}
// 主隊(duì)列 mainqueue --> 主線程 mainThread
// 注意同步dispatch_sync,因?yàn)闃I(yè)務(wù)關(guān)系,必須馬上數(shù)據(jù),所以不能異步
- (NSString*)indexTo:(NSInteger)index{
__block NSString *result = nil;
dispatch_sync(rwQueue, ^{
if (index < _safeAry.count) {
result = _safeAry[index];
}
});
return result;
}
輸出結(jié)果:

重復(fù) 執(zhí)行任務(wù)
- (void)apply_gcd{
dispatch_queue_t queue = dispatch_queue_create("testRWAry", DISPATCH_QUEUE_CONCURRENT);
// 第一個(gè)參數(shù)是重復(fù)的次數(shù)
dispatch_apply(5, queue , ^(size_t count) {
NSLog(@"%zu", count);
});
}
輸出結(jié)果:

延后 執(zhí)行任務(wù)
- (void)after_GCD{
dispatch_queue_t queue = dispatch_queue_create("testRWAry", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"start:%@", [NSThread currentThread]);
// 又重新開啟了一個(gè)線程來執(zhí)行延后的操作,所以不需要開啟RunLoop
dispatch_after(1, queue, ^{
NSLog(@"dispatch_after:%@", [NSThread currentThread]);
});
NSLog(@"end");
});
}
//只能延后執(zhí)行,不能設(shè)置延后多少秒執(zhí)行
輸出結(jié)果:

激活
注意一開始創(chuàng)建隊(duì)列,要?jiǎng)?chuàng)建成INACTIVE的
- (void)queueInactive{
// INACTIVE 未激活的,添加任務(wù)后不會(huì)自動(dòng)執(zhí)行,需要我們手動(dòng)激活過后才會(huì)執(zhí)行
dispatch_queue_t queue = dispatch_queue_create("testRWAry", DISPATCH_QUEUE_CONCURRENT_INACTIVE);
dispatch_async(queue, ^{
NSLog(@"start:%@", [NSThread currentThread]);
});
// 激活
dispatch_activate(queue);
}
傳函數(shù)指針 _f IMP
void testFunction(){
NSLog(@"testFunction::-->%@", [NSThread currentThread]);
}
// _f IMP
- (void)function_f{
dispatch_queue_t queue = dispatch_queue_create("testRWAry", DISPATCH_QUEUE_CONCURRENT);
// 第三個(gè)參數(shù)是需要傳函數(shù)指針
dispatch_async_f(queue, nil, testFunction);
}
GCD定時(shí)器
@implementation GCDTwoViewVC
- (void)viewDidLoad {
[super viewDidLoad];
queue = dispatch_queue_create("eoc_queue", DISPATCH_QUEUE_CONCURRENT);
[self timeSource];
}
// 定時(shí)器 unix (pthread) GCD()
dispatch_source_t soure;
- (void)timeSource{
// source關(guān)聯(lián)到隊(duì)列
soure = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// 配置soure的時(shí)間
/*
GCD的時(shí)間參數(shù),一般是納秒(1秒 == 10的9次方納秒) 所以要乘上 NSEC_PER_SEC
第二個(gè)參數(shù):馬上觸發(fā)
第三個(gè)參數(shù):間隔一秒
第四個(gè)參數(shù):誤差一秒
*/
dispatch_source_set_timer(soure, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 1 * NSEC_PER_SEC);
// 配置source的處理事件
dispatch_source_set_event_handler(soure, ^{
NSLog(@"soure_event:==%@", [NSThread currentThread]);
});
// 開啟定時(shí)器
dispatch_resume(soure);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// 暫停定時(shí)器
// dispatch_suspend(soure);
// 取消定時(shí)器
dispatch_cancel(soure);
}
@end
信號(hào)量 + 互斥鎖 -> 造成死鎖
在互斥鎖里面使用了信號(hào)量,容易造成死鎖的情況,當(dāng)信號(hào)量阻塞了過后,不能解鎖,也就進(jìn)行不到下一步。
#import "SignalLock.h"
#import <pthread.h>
@interface SignalLock (){
NSLock *mutexLock;
dispatch_semaphore_t sema;
int count;
}
@end
@implementation SignalLock
- (instancetype)init{
self = [super init];
if (self) {
mutexLock = [NSLock new];
sema = dispatch_semaphore_create(0);
}
return self;
}
- (void)signalLock{
[NSThread detachNewThreadSelector:@selector(threadOne) toTarget:self withObject:nil];
[NSThread detachNewThreadSelector:@selector(threadTwo) toTarget:self withObject:nil];
}
- (void)threadOne{
[self signalLockWrite];
}
- (void)threadTwo{
[self signalLockRead];
}
#pragma mark - 信號(hào)量+ 互斥鎖(死鎖)
///////// 信號(hào)量 + 互斥鎖(死鎖)
- (void)signalLockWrite{
while (1) {
// 上鎖
[mutexLock lock];
if (count >= 10) {
// 沒有內(nèi)存了
NSLog(@"空間滿了");
// 信號(hào)量阻塞,等待
// 因?yàn)檫@邊已經(jīng)上鎖了,還沒有解鎖,所以你這里阻塞了過后,不能走到下面方法的dispatch_semaphore_signal,釋放空間
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); // 阻塞
}else{
count++;
NSLog(@"空間:%d", count);
}
// 解鎖
[mutexLock unlock];
}
}
- (void)signalLockRead{
while (1) {
// 上鎖
[mutexLock lock];
if (count >= 10) {
count--;
NSLog(@"釋放空間");
// 當(dāng)上面上了鎖,并用信號(hào)量阻塞了過后,沒有解鎖,就走不到這個(gè)方法來
dispatch_semaphore_signal(sema);
}else{
count++;
}
// 解鎖
[mutexLock unlock];
}
}
@end
打印如下:

空間滿了過后,不能走到下一步的釋放空間。
條件量
為了解決上面的死鎖問題可以用條件量來解決,條件量有兩種OC的條件量和C的條件量,其中C的條件量要和C的互斥鎖結(jié)合起來使用。
#import "C_ConditionLock.h"
#import <pthread.h>
@interface C_ConditionLock(){
// OC的條件量
NSCondition *_condition;
int count;
// C的互斥鎖
pthread_mutex_t mutex; // NSLock OC的互斥鎖
// C的條件量
pthread_cond_t cond;
}
@end;
@implementation C_ConditionLock
- (instancetype)init
{
self = [super init];
if (self) {
//初始化OC的條件量
_condition = [NSCondition new];
count = 0;
//初始化C的互斥鎖和條件量
pthread_mutex_init(&mutex, 0);
pthread_cond_init(&cond, 0);
}
return self;
}
- (void)signalLock{
[NSThread detachNewThreadSelector:@selector(threadOne) toTarget:self withObject:nil];
[NSThread detachNewThreadSelector:@selector(threadTwo) toTarget:self withObject:nil];
}
- (void)conditionLockTheory{
[NSThread detachNewThreadSelector:@selector(conditionLockOne) toTarget:self withObject:nil];
[NSThread detachNewThreadSelector:@selector(conditionLockTwo) toTarget:self withObject:nil];
}
- (void)threadOne{
[self signalLockWrite];
}
- (void)threadTwo{
[self signalLockRead];
}
#pragma mark - OC條件量
- (void)signalLockWrite{
while (1) {
[_condition lock];
if (count >= 10) {
// 沒有內(nèi)存了
NSLog(@"空間滿了");
[_condition wait];
}else{
count++;
}
[_condition unlock];
}
}
- (void)signalLockRead{
while (1) {
[_condition lock];
if (count >= 10) {
count--;
NSLog(@"釋放空間");
[_condition signal];
}else{
count++;
}
[_condition unlock];
}
}
#pragma mark - OC條件量, C的實(shí)現(xiàn)
- (void)conditionLockOne{
while (1) {
// 上鎖
pthread_mutex_lock(&mutex);
if (count >= 10) {
// 沒有內(nèi)存了
NSLog(@"空間滿了");
// 阻塞,等待
pthread_cond_wait(&cond, &mutex);// 阻塞 但是互斥鎖(mutex)這個(gè)解了,即可以進(jìn)行下面的方法了,沒有被鎖住了
}else{
count++;
}
// 解鎖
pthread_mutex_unlock(&mutex);
}
}
- (void)conditionLockTwo
{
while (1) {
pthread_mutex_lock(&mutex);
if (count >= 10) {
count--;
NSLog(@"釋放空間");
// 發(fā)送信號(hào)
pthread_cond_signal(&cond);
}else{
count++;
}
pthread_mutex_unlock(&mutex);
}
}
@end
打印結(jié)果:

這樣當(dāng)空間滿了過后,就能夠釋放空間,不會(huì)造成死鎖
遞歸鎖
遞歸鎖也有OC和C兩種實(shí)現(xiàn)
#import "RecursiveLock.h"
#import <pthread.h>
/*
OC的鎖基于c的封裝,鎖對(duì)象化了
*/
@implementation RecursiveLock{
}
- (instancetype)init{
self = [super init];
if (self) {
[self oc_recursiveLockinit];
[self c_recursiveLockinit];
}
return self;
}
- (void)oc_recursiveLockinit{
// OC遞歸鎖的初始化
recursiveLock = [[NSRecursiveLock alloc] init];
}
- (void)c_recursiveLockinit{
// C遞歸鎖的初始化
pthread_mutexattr_t attr;
pthread_mutexattr_init (&attr);
pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init (&_reclock, &attr);
pthread_mutexattr_destroy (&attr);
}
// 遞歸鎖 OC
- (void)recursiveLock{
NSLog(@"start");
NSLog(@"result:%d", [self addCount:10]);
NSLog(@"end");
}
// 遞歸鎖 C
- (void)recursiveLockTheory{
NSLog(@"theory start");
NSLog(@"theory result:%d", [self addC__Count:10]);
NSLog(@"theory end");
}
// 遞歸里面做安全策略,用互斥鎖會(huì)造成死鎖的
// 遞歸鎖里可以累加鎖的數(shù)量,只要最后加鎖和解鎖數(shù)量是一樣的,就不會(huì)造成死鎖
- (int)addCount:(int)count{
// 互斥鎖,下面這行代碼連續(xù)執(zhí)行兩次就會(huì)造成死鎖
[recursiveLock lock];
if (count < 1) {
return count;
}
__block int tmp;
NSLog(@"count::%d", count);
tmp = count + [self addCount:count-1];
[recursiveLock unlock];
return tmp;
}
// C 里面遞歸鎖的實(shí)現(xiàn)
- (int)addC__Count:(int)count{
pthread_mutex_lock(&_reclock);
if (count < 1) {
return count;
}
__block int tmp;
NSLog(@"theory count::%d", count);
tmp = count + [self addC__Count:count-1];
pthread_mutex_unlock(&_reclock);
return tmp;
}
@end
OC的鎖基于C的封裝,只是將鎖對(duì)象化了。
參考文章:
iOS主線程和主隊(duì)列的區(qū)別
IOS GCD線程相關(guān)內(nèi)容(dispatch_sync,dispatch_async)
GCD 學(xué)習(xí)(二)dispatch_queue_create創(chuàng)建Dispatch Queue