最近比較清閑,寫(xiě)一下多線程GCD野哭。
多線程如果使用不當(dāng)在塔,很容易引起死鎖、數(shù)據(jù)競(jìng)爭(zhēng)拨黔、因線程太多消耗過(guò)多的內(nèi)存和因增加了cpu處理壓力造成電量消耗過(guò)快等問(wèn)題蛔溃,雖然要回避這些問(wèn)題有許多方法,但是很多都偏于復(fù)雜篱蝇,盡管極易發(fā)生很多問(wèn)題城榛,很多時(shí)候也需要使用多線程開(kāi)發(fā),因?yàn)槎嗑€程可以保證應(yīng)用的響應(yīng)性能态兴,例如:執(zhí)行時(shí)間較長(zhǎng)的處理仍然可以保證用戶的界面響應(yīng)狠持。
我們先說(shuō)GCD的隊(duì)列(Queue),隊(duì)列分為并發(fā)隊(duì)列(Concurrent)和串型隊(duì)列(Serial)瞻润。
Dispatch Queue 隊(duì)列
DISPATCH_QUEUE_SERIAL
在Serial隊(duì)列里面喘垂,使用的是一個(gè)線程,順序執(zhí)行你的操作绍撞。但是可以同時(shí)創(chuàng)建多個(gè)Serial隊(duì)列正勒,讓多個(gè)Serial同時(shí)執(zhí)行。但是我們盡量不要使用DISPATCH_QUEUE_SERIAL生成多個(gè)線程傻铣,因?yàn)檫@樣容易引起數(shù)據(jù)競(jìng)爭(zhēng)等問(wèn)題章贞。
DISPATCH_QUEUE_CONCURRENT
在Concurrent隊(duì)列里面,使用的是多個(gè)線程非洲,執(zhí)行的你的代碼塊的時(shí)候鸭限,A代碼塊執(zhí)行,不管你A塊又沒(méi)有執(zhí)行完畢两踏,B塊都開(kāi)始執(zhí)行败京,也就是使用多個(gè)線程同時(shí)執(zhí)行多個(gè)處理。
dispatch_queue_create
通過(guò)dispatch_queue_create可以創(chuàng)建線程隊(duì)列梦染,例如:
dispatch_queue_t queue = dispatch_queue_create("Concurrent.Queue", DISPATCH_QUEUE_CONCURRENT);
這個(gè)創(chuàng)建的線程隊(duì)列對(duì)象名字叫 queue赡麦,是一個(gè)并發(fā)隊(duì)列朴皆,如果將他加入一段代碼,執(zhí)行的順序是這樣的
dispatch_queue_t queue = dispatch_queue_create("Concurrent.Queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
/*
執(zhí)行耗時(shí)操作*/
for (NSInteger i = 0; i < 10000; i++) {
NSLog(@"%ld", (long)i);
}
});
/*
響應(yīng)其他處理*/
NSLog(@"在隊(duì)列queue未執(zhí)行完畢時(shí)候泛粹,就已經(jīng)開(kāi)始執(zhí)行這個(gè)打印");
上述代碼打印的結(jié)果是:
Main Dispatch Queue
Main Dispatch Queue是主線程中執(zhí)行的線程隊(duì)列遂铡,其獲取方法是系統(tǒng)已經(jīng)預(yù)置好的dispatch_get_main_queue()。
這個(gè)線程的主要用于,當(dāng)開(kāi)啟多個(gè)線程處理耗時(shí)操作晶姊,當(dāng)其中一個(gè)耗時(shí)操作處理完畢需要刷新UI時(shí)扒接,就需要在dispatch_get_main_queue()中刷新,因?yàn)閁I只能在主線程中刷新帽借。
獲取方法:
dispatch_queue_t main = dispatch_get_main_queue();
Global Dispatch Queue
Global Dispatch Queue是Concurrent類型線程隊(duì)列珠增,Global Dispatch Queue 有4個(gè)優(yōu)先級(jí)超歌,如下:
#define DISPATCH_QUEUE_PRIORITY_HIGH 2 /*高優(yōu)先級(jí)*/
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 /*默認(rèn)優(yōu)先級(jí)*/
#define DISPATCH_QUEUE_PRIORITY_LOW (-2) /*低優(yōu)先級(jí)*/
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN /*后臺(tái)優(yōu)先級(jí)*/
雖然Global有4個(gè)優(yōu)先級(jí)砍艾,但是根據(jù)以往經(jīng)驗(yàn)和查閱的資料來(lái)判斷,這4個(gè)優(yōu)先級(jí)并不能保證實(shí)時(shí)性巍举,只能作為大致脆荷、大概的判斷,比如這個(gè)處理的內(nèi)容要求盡可能的最先處理完畢懊悯,那么我們就使用DISPATCH_QUEUE_PRIORITY_HIGH
蜓谋,如果處理的內(nèi)容可有可無(wú),則可以用DISPATCH_QUEUE_PRIORITY_BACKGROUND
炭分。
示例如下:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
/*
執(zhí)行耗時(shí)操作*/
for (NSInteger i = 0; i < 10000; i++) {
NSLog(@"%ld", (long)i);
}
/*
進(jìn)入主線程桃焕,例如刷新UI*/
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"可以執(zhí)行主線程操作");
});
});
打印結(jié)果如圖2
dispatch_after
dispatch_after可以執(zhí)行指定時(shí)間的操作,在多少時(shí)間以后執(zhí)行操作捧毛,所以他有以下四個(gè)時(shí)間單位
SEC:秒
MSEC:毫秒
USEC:微秒
NSEC:納秒
PER:每
#define NSEC_PER_SEC 1000000000ull /*每秒有多少納秒观堂,使用的時(shí)候,這個(gè)就是最常用的秒*/
#define NSEC_PER_MSEC 1000000ull /*每毫秒有多少納秒*/
#define USEC_PER_SEC 1000000ull /*每秒有多少微秒呀忧。(注意是指在納秒的基礎(chǔ)上)*/
#define NSEC_PER_USEC 1000ull /*每微秒有多少納秒*/
獲取時(shí)間的節(jié)點(diǎn)有兩個(gè)
#define DISPATCH_TIME_NOW (0ull) /*當(dāng)前*/
#define DISPATCH_TIME_FOREVER (~0ull) /*在遙遠(yuǎn)的未來(lái)*/
值得注意的是师痕,Main Dispatch Queue是在主線程的RunLoop執(zhí)行的,所以在dispatch_after
線程Block最快要在5秒執(zhí)行(假設(shè)我們?cè)O(shè)置的dispatch_time_t 是5秒)而账,但是可能因?yàn)橹骶€程因有大量的處理或者主線程本身就有以下延時(shí)操作胰坟,那么等待的時(shí)間將會(huì)更長(zhǎng)。
代碼示例:
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"hh:mm:ss:SSS"];
NSDate *datenow = [NSDate date];
NSString *nowtimeStr = [formatter stringFromDate:datenow];
NSLog(@"now = %@",nowtimeStr);
/*延時(shí)操作*/
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSDate *dateNow2 = [NSDate date];
NSString *nowtimeStr2 = [formatter stringFromDate:dateNow2];
NSLog(@"now2 = %@",nowtimeStr2);
});
根據(jù)打印結(jié)果可以得知泞辐,延時(shí)操作并不是絕對(duì)的5秒笔横,雖然如此,但是如果只是想大致的執(zhí)行延時(shí)處理咐吼,這個(gè)函數(shù)還是可以的狠裹,打印結(jié)果如圖3。
Dispatch Group
Group線程是當(dāng)我們指定幾個(gè)線程汽烦,可以是異步的耗時(shí)操作涛菠,當(dāng)幾個(gè)耗時(shí)操作執(zhí)行完畢后再執(zhí)行。
先說(shuō)一下幾個(gè)關(guān)鍵函數(shù):
/*Group線程對(duì)象*/
dispatch_group_t;
/*生成Group線程對(duì)象的方法,使用方法和dispatch_get_main_queue()一樣*/
dispatch_group_create(void);
/*該函數(shù)與dispatch_async函數(shù)相同俗冻,但是dispatch_group_async有兩個(gè)參數(shù)礁叔,第一個(gè)要填寫(xiě)創(chuàng)建的dispatch_group_t類型的對(duì)象,第二個(gè)dispatch_queue_t類型的對(duì)象*/
void dispatch_group_async(dispatch_group_t group,
dispatch_queue_t queue,
dispatch_block_t block);
/*該函數(shù)是當(dāng)所有的dispatch_group_async函數(shù)執(zhí)行完畢后迄薄,通知到這個(gè)函數(shù)琅关,然后執(zhí)行block里面的代碼塊,需要填寫(xiě)兩個(gè)參數(shù)讥蔽,第一個(gè)填寫(xiě)dispatch_group_t類型涣易,第二個(gè)dispatch_queue_t類型的對(duì)象*/
void dispatch_group_notify(dispatch_group_t group,
dispatch_queue_t queue,
dispatch_block_t block);
/*dispatch_group_wait需要填寫(xiě)兩個(gè)參數(shù),第一個(gè)參數(shù)dispatch_group_t類型冶伞,第二個(gè)參數(shù)參考dispatch_after線程的時(shí)間參數(shù)新症,如果dispatch_time_t類型填寫(xiě)成DISPATCH_TIME_FOREVER,那么就會(huì)等待所有的dispatch_group_async線程執(zhí)行完畢才會(huì)執(zhí)行判斷响禽,如果填寫(xiě)DISPATCH_TIME_FOREVER不如使用dispatch_group_notify函數(shù)*/
long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);
以下分別是dispatch_group_notify
和dispatch_group_wait
兩個(gè)函數(shù)的代碼示例徒爹,具體使用根據(jù)業(yè)務(wù)和需求使用。
/*dispatch_group_notify示例*/
/*創(chuàng)建Dispatch Group線程*/
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0 ; i < 5; i++) {
NSLog(@"--------1-------- %@",[NSThread currentThread]);
}
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0 ; i < 10; i++) {
NSLog(@"--------2-------- %@",[NSThread currentThread]);
}
});
/*當(dāng)1芋类,2完成后再執(zhí)行3*/
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"--------3-------- %@",[NSThread currentThread]);
/*打印結(jié)果圖4*/
});
/*dispatch_group_wait示例*/
/*創(chuàng)建Dispatch Group線程*/
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0 ; i < 5; i++) {
NSLog(@"--------1-------- %@", [NSThread currentThread]);
}
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0 ; i < 10; i++) {
NSLog(@"--------2-------- %@", [NSThread currentThread]);
}
});
/*設(shè)置時(shí)間為1秒鐘*/
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC);
long result = dispatch_group_wait(group, time);
/*在1秒鐘內(nèi)隆嗅,1和2全部執(zhí)行完畢得到long值為0*/
/*打印結(jié)果圖5*/
if (!result) {
NSLog(@"dispatch_group_async全部執(zhí)行完畢 %@", [NSThread currentThread]);
} else {
NSLog(@"dispatch_group_async還有在執(zhí)行的,但是等到時(shí)間到了 %@", [NSThread currentThread]);
}
/*將循環(huán)次數(shù)增大侯繁,增加循環(huán)打印時(shí)間超過(guò)1秒的情況*/
/*創(chuàng)建Dispatch Group線程*/
dispatch_group_t group = dispatch_group_create();
/*將循環(huán)次數(shù)增大*/
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0 ; i < 5000; i++) {
NSLog(@"--------1-------- %@", [NSThread currentThread]);
}
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0 ; i < 10000; i++) {
NSLog(@"--------2-------- %@", [NSThread currentThread]);
}
});
/*設(shè)置時(shí)間為1秒鐘*/
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC);
long result = dispatch_group_wait(group, time);
/*在1秒鐘內(nèi)胖喳,1和2沒(méi)有執(zhí)行完畢得到long值為>0*/
/*打印結(jié)果圖6*/
if (!result) {
NSLog(@"dispatch_group_async全部執(zhí)行完畢 %@", [NSThread currentThread]);
} else {
NSLog(@"dispatch_group_async還有在執(zhí)行的,但是等到時(shí)間到了 %@", [NSThread currentThread]);
}