寫(xiě)在前面:
GCD祖凫,它是基于C語(yǔ)言的API寝杖,開(kāi)發(fā)者只需要將任務(wù)放在block內(nèi)型豁,并指定好追加的隊(duì)列僵蛛,就可以完成多線程開(kāi)發(fā)。屬于系統(tǒng)級(jí)的線程管理迎变,在Dispatch queue中執(zhí)行需要執(zhí)行的任務(wù)性能非常的高充尉。以下為個(gè)人的學(xué)習(xí)筆記,大神略過(guò)衣形!
隊(duì)列
隊(duì)列:Dispatch Queue是執(zhí)行處理的等待隊(duì)列驼侠,按照任務(wù)(block)追加到隊(duì)列里的順序,并遵循FIFO執(zhí)行處理
- 隊(duì)列分為串行隊(duì)列和并行隊(duì)列谆吴。
- Serial Dispatch Queue:串行隊(duì)列倒源,等待當(dāng)前執(zhí)行任務(wù)處理結(jié)束的隊(duì)列。
輸出:- (void)gcd_serialQueue { //第一個(gè)函數(shù)為隊(duì)列的名稱句狼, // 第二個(gè)參數(shù)是NULL和DISPATCH_QUEUE_SERIAL時(shí)笋熬,返回的隊(duì)列就是串行隊(duì)列。 dispatch_queue_t queue = dispatch_queue_create("serial queue", NULL); for (NSInteger i = 0; i < 10; i ++) { dispatch_async(queue, ^{ NSLog(@"serial queue :%ld",i); }); } }
Study_GCD[46280:2496418] serial queue :0 Study_GCD[46280:2496418] serial queue :1 Study_GCD[46280:2496418] serial queue :2 Study_GCD[46280:2496418] serial queue :3 Study_GCD[46280:2496418] serial queue :4 Study_GCD[46280:2496418] serial queue :5 Study_GCD[46280:2496418] serial queue :6 Study_GCD[46280:2496418] serial queue :7 Study_GCD[46280:2496418] serial queue :8 Study_GCD[46280:2496418] serial queue :9
- Concurrent Dispatch Queue:并發(fā)隊(duì)列腻菇,不等待當(dāng)前執(zhí)行任務(wù)處理結(jié)束的隊(duì)列胳螟。
- (void)gcd_concurrentQueue { dispatch_queue_t queue = dispatch_queue_create("concurrent queue", DISPATCH_QUEUE_CONCURRENT); for (NSInteger i = 0; i < 6; i ++) { dispatch_async(queue, ^{ NSLog(@"concurrent queue %ld ",i); }); } }
輸出:
Study_GCD[46343:2501196] concurrent queue 0
Study_GCD[46343:2501197] concurrent queue 3
Study_GCD[46343:2501215] concurrent queue 2
Study_GCD[46343:2501214] concurrent queue 1
Study_GCD[46343:2501199] concurrent queue 4
Study_GCD[46343:2501217] concurrent queue 5
- 放入串行隊(duì)列的任務(wù)將會(huì)在主線程執(zhí)行,執(zhí)行順序是按照順序執(zhí)行筹吐。
- 放入并行隊(duì)列的任務(wù)會(huì)在子線程執(zhí)行糖耸,執(zhí)行順序是并行執(zhí)行。
主線程&子線程
主線程:負(fù)責(zé)執(zhí)行UI活動(dòng)丘薛,絕大部分的UI活動(dòng)都要在這里調(diào)用嘉竟,不能讓其阻塞,要將耗時(shí)的任務(wù)放到子線程來(lái)做。
子線程:負(fù)責(zé)執(zhí)行耗時(shí)的運(yùn)算周拐,網(wǎng)絡(luò)請(qǐng)求等不能放在主線程的任務(wù)铡俐。
系統(tǒng)為我們提供了共用的主隊(duì)列(Main Dispatch Queue)和全局并行隊(duì)列(Global Dispatch Queue)。我們只需將需要執(zhí)行的任務(wù)放入到這兩類隊(duì)列里就可以實(shí)現(xiàn)多線程編程妥粟。
系統(tǒng)提供的隊(duì)列
Main Dispatch Queue
- 主隊(duì)列:
放在這個(gè)隊(duì)列里的任務(wù)會(huì)追加到主線程的RunLoop中執(zhí)行审丘。需要刷新UI的時(shí)候我們可以直接獲取這個(gè)隊(duì)列,將任務(wù)追加到這個(gè)隊(duì)列中勾给。
dispatch_queue_t mainQ = dispatch_get_main_queue();
NSOperationQueue *mainQ = [NSOperationQueue mainQueue];
- 獲取主隊(duì)列并布置任務(wù)
//NSThread
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
//GCD
dispatch_async(dispatch_get_main_queue(), ^{
[doSomething];
});
Globle Dispatch Queue
- 全局并發(fā)隊(duì)列:
開(kāi)發(fā)者可以不需要特意通過(guò)dispatch_queue_create方法創(chuàng)建一個(gè)Concurrent Dispatch Queue滩报,可以將任務(wù)直接放在這個(gè)全局并發(fā)隊(duì)列里面。
dispatch queue_t globalDispatchQueueDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
- 獲取全局并行隊(duì)列并布置任務(wù)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[doSomething];
});
線程通訊
很多情況下播急,我們需要在子線程進(jìn)行下載任務(wù)脓钾,下載完成后在主線程更新UI,這時(shí)候就需要線程之間的通信:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//下載圖片
NSData *dataFromURL = [NSData dataWithContentsOfURL:imageURL];
UIImage *imageFromData = [UIImage imageWithData:dataFromURL];
dispatch_async(dispatch_get_main_queue(), ^{
//加載完成更新view
UIImageView *imageView = [[UIImageView alloc] initWithImage:imageFromData];
});
});
//在這里桩警,我們?cè)谌植⑿嘘?duì)列的回調(diào)block里調(diào)用了主線程可训,并在主線程里執(zhí)行了UI操作。
dispatch_group
需求點(diǎn):執(zhí)行多個(gè)耗時(shí)的異步任務(wù)捶枢,但是只能等到這些任務(wù)都執(zhí)行完畢后握截,才能在主線程執(zhí)行某個(gè)任務(wù)。
為了實(shí)現(xiàn)這個(gè)需求烂叔,我們需要讓將這些異步執(zhí)行的操作放在
- dispatch_group_async函數(shù)中執(zhí)行谨胞,最后再調(diào)用,
- dispatch_group_notify來(lái)執(zhí)行最后執(zhí)行的任務(wù)蒜鸡。
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執(zhí)行1個(gè)耗時(shí)的異步操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執(zhí)行1個(gè)耗時(shí)的異步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的異步操作都執(zhí)行完畢后胯努,回到主線程...
});
dispatch_after
- 在某個(gè)線程里,在指定的時(shí)間后處理某個(gè)任務(wù):
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"2秒之后追加到隊(duì)列");
});
dispatch_barrier_async
- 關(guān)于解決數(shù)據(jù)競(jìng)爭(zhēng)的方法:讀取處理是可以并發(fā)的逢防,但是寫(xiě)入處理卻是不允許并發(fā)執(zhí)行的叶沛。
- 讀取處理追加到concurrent dispatch queue中
- 寫(xiě)入處理在任何一個(gè)讀取處理沒(méi)有執(zhí)行的狀態(tài)下,追加到serial dispatch queue中(也就是說(shuō)忘朝,在寫(xiě)入處理結(jié)束之前灰署,讀取處理不可執(zhí)行)。
需求點(diǎn):雖然我們有時(shí)要執(zhí)行幾個(gè)不同的異步任務(wù)辜伟,但是我們還是要將其分成兩組:當(dāng)?shù)谝唤M異步任務(wù)都執(zhí)行完成后才執(zhí)行第二組的異步任務(wù)氓侧。這里的組可以包含一個(gè)任務(wù)脊另,也可以包含多個(gè)任務(wù)导狡。
為了實(shí)現(xiàn)這個(gè)需求,我們需要使用dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);在兩組任務(wù)之間形成“柵欄”偎痛,使其“下方”的異步任務(wù)在其“上方”的異步任務(wù)都完成之前是無(wú)法執(zhí)行的旱捧。
dispatch_queue_t queue = dispatch_queue_create("1122333", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"----任務(wù) 1-----");
});
dispatch_async(queue, ^{
NSLog(@"----任務(wù) 2-----");
});
dispatch_barrier_async(queue, ^{
NSLog(@"----barrier-----");
});
dispatch_async(queue, ^{
NSLog(@"----任務(wù) 3-----");
});
dispatch_async(queue, ^{
NSLog(@"----任務(wù) 4-----");
});
輸出
Study_GCD[46399:2506252] ----任務(wù) 2-----
Study_GCD[46399:2506249] ----任務(wù) 1-----
Study_GCD[46399:2506249] ----barrier-----
Study_GCD[46399:2506249] ----任務(wù) 3-----
Study_GCD[46399:2506252] ----任務(wù) 4-----
dispatch_sync
- dispatch_async:異步函數(shù),這個(gè)函數(shù)會(huì)立即返回,不做任何等待枚赡,它所指定的block“非同步地”追加到指定的隊(duì)列中氓癌。
- dispatch_sync:同步函數(shù),這個(gè)函數(shù)不會(huì)立即返回贫橙,它會(huì)一直等待追加到特定隊(duì)列中的制定block完成工作后才返回贪婉,所以它的目的(也是效果)是阻塞當(dāng)前線程。(開(kāi)發(fā)中切記注意!)
dispatch_once (一次性任務(wù))
- 通過(guò)dispatch_once處理的代碼只執(zhí)行一次卢肃,而且是線程安全的:
- (void)gcd_dispatch_once_1
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (NSInteger index = 0; index < 5; index++) {
dispatch_async(queue, ^{
[self gcd_onceCode];
});
}
}
- (void)gcd_onceCode
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"只執(zhí)行一次的代碼");
});
}
輸出:
Study_GCD[46436:2509090] 只執(zhí)行一次的代碼