iOS實(shí)現(xiàn)多線(xiàn)程的4套方案
1睹逃、pthread
01 特點(diǎn):
(1)一套通用的多線(xiàn)程api
(2)適用于Unix\Linux\Windows等系統(tǒng)
(3)跨平臺(tái)拇囊,可移植
(4)使用難度大
02 使用語(yǔ)言:c語(yǔ)言
03 使用頻率:幾乎不用
04 線(xiàn)程生命周期:用程序員進(jìn)行管理
2扶踊、NSThread
01 特點(diǎn)
(1)使用更加面對(duì)對(duì)象
(2)簡(jiǎn)單易用澄峰,可直接操作線(xiàn)程對(duì)象
02 使用語(yǔ)言:oc
03 使用頻率:偶爾
04 線(xiàn)程生命周期:用程序員進(jìn)行管理
3凯旋、GCD
01 特點(diǎn):
(1)旨在替代NSThread等線(xiàn)程技術(shù)
(2)充分利用設(shè)備的多核(自動(dòng))
02 使用語(yǔ)言:oc
03 使用頻率:經(jīng)常
04 線(xiàn)程生命周期:自動(dòng)管理
4馋没、NSOperation & NSOperationQueue
01 特點(diǎn):
(1)基于GCD(底層是GCD)
(2)比GCD多了一些更簡(jiǎn)單使用的功能
(3)使用更加面對(duì)對(duì)象
02 使用語(yǔ)言:oc
03 使用頻率:經(jīng)常
04 線(xiàn)程生命周期:自動(dòng)管理
使用:
1昔逗、pthread
這種方式只是告訴大家有這么個(gè)東西,一般不用的
這是一套在很多操作系統(tǒng)上都通用的的多線(xiàn)程API篷朵,所以移植性很強(qiáng)(然并卵)勾怒,基于C語(yǔ)言
首先導(dǎo)入頭文件
import <ptthread.h>
創(chuàng)建線(xiàn)程并執(zhí)行任務(wù)
//創(chuàng)建線(xiàn)程
pthread_tthread;
NSString* name =@"test";
/*
參數(shù)一:線(xiàn)程對(duì)象
參數(shù)二:線(xiàn)程屬性
參數(shù)三:void*(*)(viod*)指向函數(shù)的指針
參數(shù)四:函數(shù)的參數(shù)(可有可無(wú),可以傳NULL)
*/
pthread_create(&thread,NULL,run, (__bridgevoid*)name);
void*run(void*param)
{
NSLog(@"---%@-%@",[NSThreadcurrentThread],param);
//for (NSInteger i =0 ; i<10; i++) {
//NSLog(@"%zd--%@-%@",i,[NSThread currentThread],param);
//}
returnNULL;
}
2声旺、NSThread
經(jīng)蘋(píng)果封裝后笔链,完全面向?qū)ο蟆腮猖?梢灾苯硬倏鼐€(xiàn)程對(duì)象鉴扫,直觀方便。生命周期需要手動(dòng)管理澈缺,偶爾使用坪创。比如[NSThread currentThread],它可以獲取當(dāng)前線(xiàn)程類(lèi)姐赡,你就可以知道當(dāng)前線(xiàn)程的各種屬性莱预,用于調(diào)試十分方便。下面來(lái)看看它的一些用法项滑。
(1)NSThread的基本使用
//下面的三種方式創(chuàng)建的線(xiàn)程都會(huì)放在子線(xiàn)程中執(zhí)行
//第一種創(chuàng)建線(xiàn)程的方式:alloc init.
//特點(diǎn):需要手動(dòng)開(kāi)啟線(xiàn)程依沮,可以拿到線(xiàn)程對(duì)象進(jìn)行詳細(xì)設(shè)置
/*
第一個(gè)參數(shù):目標(biāo)對(duì)象
第二個(gè)參數(shù):選擇器,線(xiàn)程啟動(dòng)要調(diào)用哪個(gè)方法
第三個(gè)參數(shù):前面方法要接收的參數(shù)(最多只能接收一個(gè)參數(shù)枪狂,沒(méi)有則傳nil)
*/
NSThread * thread = [[NSThread alloc]initWithTarget:self selector:@selector(run1:) object:@"子線(xiàn)程1"];
//線(xiàn)程啟動(dòng)
[thread start];
//第二種創(chuàng)建線(xiàn)程的方式:分離出一條子線(xiàn)程
//特點(diǎn):自動(dòng)啟動(dòng)線(xiàn)程危喉,無(wú)法對(duì)線(xiàn)程進(jìn)行更詳細(xì)的設(shè)置
/*
第一個(gè)參數(shù):線(xiàn)程啟動(dòng)調(diào)用的方法
第二個(gè)參數(shù):目標(biāo)對(duì)象
第三個(gè)參數(shù):傳遞給調(diào)用方法的參數(shù)
*/
[NSThread detachNewThreadSelector:@selector(run2:) toTarget:self withObject:@"子線(xiàn)程2"];
//第三種創(chuàng)建線(xiàn)程的方式:后臺(tái)線(xiàn)程
//特點(diǎn):自動(dòng)啟動(dòng)線(xiàn)程,無(wú)法進(jìn)行更詳細(xì)設(shè)置
[self performSelectorInBackground :@selector(run3:) withObject:@"子線(xiàn)程3"];
}
(2)線(xiàn)程的狀態(tài)及設(shè)置
//線(xiàn)程的各種狀態(tài):新建-就緒-運(yùn)行-阻塞-死亡
//常用的控制線(xiàn)程狀態(tài)的方法
[NSThread exit];//退出當(dāng)前線(xiàn)程
[NSThread sleepForTimeInterval:2.0];//阻塞線(xiàn)程
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];//阻塞線(xiàn)程
//注意:線(xiàn)程死了不能復(fù)生
//設(shè)置線(xiàn)程的屬性
//設(shè)置線(xiàn)程的名稱(chēng)
thread.name = @"線(xiàn)程A";
//設(shè)置線(xiàn)程的優(yōu)先級(jí),注意線(xiàn)程優(yōu)先級(jí)的取值范圍為0.0~1.0之間州疾,1.0表示線(xiàn)程的優(yōu)先級(jí)最高,如果不設(shè)置該值辜限,那么理想狀態(tài)下默認(rèn)為0.5
thread.threadPriority = 1.0;
(3)線(xiàn)程通信
NSURL *url = [NSURL URLWithString:@"http://p6.qhimg.com/t01d2954e2799c461ab.jpg"];
//2.根據(jù)url地址下載圖片數(shù)據(jù)到本地(二進(jìn)制數(shù)據(jù)
NSData *data = [NSData dataWithContentsOfURL:url];
//3.把下載到本地的二進(jìn)制數(shù)據(jù)轉(zhuǎn)換成圖片
UIImage *image = [UIImage imageWithData:data];
//4.回到主線(xiàn)程刷新UI
//4.1 第一種方式
// [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];
//4.2 第二種方式
// [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];
//4.3 第三種方式
[self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
(4)線(xiàn)程安全
01 前提:多個(gè)線(xiàn)程訪(fǎng)問(wèn)同一塊資源會(huì)發(fā)生數(shù)據(jù)安全問(wèn)題
02 解決方案:加互斥鎖
03 相關(guān)代碼:@synchronized(鎖對(duì)象){//需要鎖定的代碼}
04 專(zhuān)業(yè)術(shù)語(yǔ)-線(xiàn)程同步
05 原子和非原子屬性(是否對(duì)setter方法加鎖)
3、GCD
Grand Central Dispatch 孝治,蘋(píng)果為多核的并行運(yùn)算提出的解決方案列粪,所以會(huì)自動(dòng)合理的利用更多的CPU內(nèi)核(比如雙核审磁、四核),它會(huì)自動(dòng)管理線(xiàn)程的生命周期(創(chuàng)建線(xiàn)程岂座、調(diào)度任務(wù)态蒂、銷(xiāo)毀線(xiàn)程),不需要管理费什,只需告訴要干什么就行钾恢。同時(shí)使用的是C語(yǔ)言,不過(guò)由于使用了Block鸳址,使用起來(lái)更加方便靈活瘩蚪。
(1)基本知識(shí)
異步async:會(huì)開(kāi)啟新線(xiàn)程執(zhí)行,多線(xiàn)程的代名詞稿黍。
串行隊(duì)列同步執(zhí)行:不開(kāi)線(xiàn)程疹瘦,在原來(lái)的線(xiàn)程中一個(gè)一個(gè)順序執(zhí)行
串行隊(duì)列異步執(zhí)行:開(kāi)一個(gè)線(xiàn)程,在新的線(xiàn)程中一個(gè)一個(gè)順序執(zhí)行
并發(fā)隊(duì)列同步執(zhí)行:不開(kāi)線(xiàn)程巡球,在原來(lái)的線(xiàn)程中一個(gè)一個(gè)順序執(zhí)行
并發(fā)隊(duì)列異步執(zhí)行:開(kāi)多個(gè)線(xiàn)程言沐,并發(fā)執(zhí)行(不一定是一個(gè)一個(gè)執(zhí)行);
執(zhí)行任務(wù)方法決定開(kāi)不開(kāi)線(xiàn)程酣栈,同步不開(kāi)線(xiàn)程险胰,異步開(kāi)線(xiàn)程
隊(duì)列決定開(kāi)線(xiàn)程個(gè)數(shù),串行最多開(kāi)一個(gè)線(xiàn)程矿筝,并發(fā)可以開(kāi)多個(gè)線(xiàn)程起便。具體開(kāi)多少個(gè),由GCD底層決定窖维,程序員不能控制榆综。
隊(duì)列選擇
串行隊(duì)列異步執(zhí)行:
開(kāi)一條線(xiàn)程,順序執(zhí)行
效率不高陈辱,執(zhí)行比較慢奖年,資源占用小,省電
使用場(chǎng)合:網(wǎng)絡(luò)3G沛贪,效果要求不高
并發(fā)隊(duì)列異步執(zhí)行:
開(kāi)啟多條線(xiàn)程陋守,順序執(zhí)行
效率高,執(zhí)行快利赋,資源消耗大水评,費(fèi)電
使用場(chǎng)合:無(wú)線(xiàn)網(wǎng)絡(luò)或者需要很快響應(yīng),要求用戶(hù)體驗(yàn)流暢
對(duì)任務(wù)執(zhí)行順序沒(méi)有要求
同步任務(wù):
一般只會(huì)在并發(fā)隊(duì)列媚送,需要阻塞后續(xù)任務(wù)中燥。必須等待同步任務(wù)執(zhí)行完成,再去執(zhí)行其他任務(wù)塘偎×粕妫“依賴(lài)”關(guān)系
GCD 與NSOperation比較
GCD :
將任務(wù)(block)添加到隊(duì)列(串行/并發(fā))拿霉,指定執(zhí)行任務(wù)的方法(同步/異步)
拿到disoatch_get_main_queue(),線(xiàn)程間通信
NSOperation:
將操作(異步執(zhí)行)添加到隊(duì)列(并發(fā)/全局)
[NSOperationQueue mainQueue] 主隊(duì)列,任務(wù)添加到主隊(duì)列咱扣,就會(huì)在主線(xiàn)程執(zhí)行
提供了GCD不好實(shí)現(xiàn)的“最大并發(fā)數(shù)”
暫停/繼續(xù) ——掛起
取消所有任務(wù)
依賴(lài)關(guān)系
01 兩個(gè)核心概念-隊(duì)列和任務(wù)
任務(wù):即操作绽淘,你想要干什么,說(shuō)白了就是一段代碼闹伪,在 GCD 中就是一個(gè) Block沪铭,所以添加任務(wù)十分方便。任務(wù)有兩種執(zhí)行方式: 同步執(zhí)行 和 異步執(zhí)行偏瓤,他們之間的區(qū)別是 是否會(huì)創(chuàng)建新的線(xiàn)程(是否阻塞當(dāng)前線(xiàn)程)杀怠。
同步執(zhí)行:只要是同步執(zhí)行的任務(wù),都會(huì)在當(dāng)前線(xiàn)程執(zhí)行厅克,不會(huì)另開(kāi)線(xiàn)程赔退。
異步執(zhí)行:只要是異步執(zhí)行的任務(wù),都會(huì)另開(kāi)線(xiàn)程已骇,在別的線(xiàn)程執(zhí)行离钝。
隊(duì)列:用于存放任務(wù)票编。一共有兩種隊(duì)列褪储, 串行隊(duì)列 和 并行隊(duì)列
串行隊(duì)列 中的任務(wù)會(huì)根據(jù)隊(duì)列的定義 FIFO 的執(zhí)行,一個(gè)接一個(gè)的先進(jìn)先出的進(jìn)行執(zhí)行
放到串行隊(duì)列的任務(wù)慧域,GCD 會(huì) FIFO(先進(jìn)先出)地取出來(lái)一個(gè)鲤竹,執(zhí)行一個(gè),然后取下一個(gè)昔榴,這樣一個(gè)一個(gè)的執(zhí)行辛藻。
并行隊(duì)列 中的任務(wù) 根據(jù)同步或異步有不同的執(zhí)行方式。
放到并行隊(duì)列的任務(wù)互订,GCD 也會(huì) FIFO的取出來(lái)吱肌,但不同的是,它取出來(lái)一個(gè)就會(huì)放到別的線(xiàn)程仰禽,然后再取出來(lái)一個(gè)又放到另一個(gè)的線(xiàn)程氮墨。這樣由于取的動(dòng)作很快,忽略不計(jì)吐葵,看起來(lái)规揪,所有的任務(wù)都是一起執(zhí)行的。不過(guò)需要注意温峭,GCD 會(huì)根據(jù)系統(tǒng)資源控制并行的數(shù)量猛铅,所以如果任務(wù)很多,它并不會(huì)讓所有任務(wù)同時(shí)執(zhí)行凤藏。
02 同步函數(shù)和異步函數(shù)
同步(sync) 和 異步(async) 的主要區(qū)別在于會(huì)不會(huì)阻塞當(dāng)前線(xiàn)程奸忽,直到 Block 中的任務(wù)執(zhí)行完畢堕伪!
如果是 同步(sync) 操作,它會(huì)阻塞當(dāng)前線(xiàn)程并等待 Block 中的任務(wù)執(zhí)行完畢栗菜,然后當(dāng)前線(xiàn)程才會(huì)繼續(xù)往下運(yùn)行刃跛。
如果是 異步(async)操作,當(dāng)前線(xiàn)程會(huì)直接往下執(zhí)行苛萎,它不會(huì)阻塞當(dāng)前線(xiàn)程桨昙。
(2)GCD基本使用
01 異步函數(shù)+并發(fā)隊(duì)列:開(kāi)啟多條線(xiàn)程,并發(fā)執(zhí)行任務(wù)
02 異步函數(shù)+串行隊(duì)列:開(kāi)啟一條線(xiàn)程腌歉,串行執(zhí)行任務(wù)
03 同步函數(shù)+并發(fā)隊(duì)列:不開(kāi)線(xiàn)程蛙酪,串行執(zhí)行任務(wù)
04 同步函數(shù)+串行隊(duì)列:不開(kāi)線(xiàn)程,串行執(zhí)行任務(wù)
05 異步函數(shù)+主隊(duì)列:不開(kāi)線(xiàn)程翘盖,在主線(xiàn)程中串行執(zhí)行任務(wù)
06 同步函數(shù)+主隊(duì)列:不開(kāi)線(xiàn)程桂塞,串行執(zhí)行任務(wù)(注意死鎖發(fā)生)
07 注意同步函數(shù)和異步函數(shù)在執(zhí)行順序上面的差異
(3)GCD線(xiàn)程間通信
//0.獲取一個(gè)全局的隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//1.先開(kāi)啟一個(gè)線(xiàn)程,把下載圖片的操作放在子線(xiàn)程中處理
dispatch_async(queue, ^{
//2.下載圖片
NSURL *url = [NSURL URLWithString:@"http://h.hiphotos.baidu.com/zhidao/pic/item/6a63f6246b600c3320b14bb3184c510fd8f9a185.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
NSLog(@"下載操作所在的線(xiàn)程--%@",[NSThread currentThread]);
//3.回到主線(xiàn)程刷新UI
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
//打印查看當(dāng)前線(xiàn)程
NSLog(@"刷新UI---%@",[NSThread currentThread]);
});
});
4)GCD其它常用函數(shù)
01 柵欄函數(shù)(控制任務(wù)的執(zhí)行順序)
dispatch_barrier_async(queue, ^{
NSLog(@"--dispatch_barrier_async-");
});
02 延遲執(zhí)行(延遲·控制在哪個(gè)線(xiàn)程執(zhí)行)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"---%@",[NSThread currentThread]);
});
03 一次性代碼(注意不能放到懶加載)
-(void)once
{
//整個(gè)程序運(yùn)行過(guò)程中只會(huì)執(zhí)行一次
//onceToken用來(lái)記錄該部分的代碼是否被執(zhí)行過(guò)
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"-----");
});
}
04 快速迭代(開(kāi)多個(gè)線(xiàn)程并發(fā)完成迭代操作)
dispatch_apply(subpaths.count, queue, ^(size_t index) {
});
05 隊(duì)列組
//創(chuàng)建隊(duì)列組
dispatch_group_t group = dispatch_group_create();
//隊(duì)列組中的任務(wù)執(zhí)行完畢之后馍驯,執(zhí)行該函數(shù)
dispatch_group_notify(dispatch_group_t group,
dispatch_queue_t queue,
dispatch_block_t block);
06 進(jìn)入和離開(kāi)隊(duì)列
dispatch_group_enter(group);
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0; i < 200; i ++) {
NSLog(@"%s,line num = %d \n %@",__func__,i,[NSThread currentThread]);
}
dispatch_group_leave(group);
});
dispatch_group_enter(group);
for (int i = 0 ; i< 10000; i++) {
NSLog(@"%s,line num = %d \n %@",__func__,i,[NSThread currentThread]);
if (i == 9999) {
dispatch_group_leave(group);
}
}
dispatch_group_notify(group,dispatch_get_main_queue(),^{
NSLog(@"%s,line num = %d \n %@",__func__,__LINE__,@"main123");
});
5)GCD定時(shí)器
大多數(shù)情況是我們使用定時(shí)器都是NSTimer阁危,但是在一些情況下NSTimer誤差會(huì)比較嚴(yán)重(這個(gè)問(wèn)題會(huì)在runloop 章節(jié)說(shuō)明),這時(shí)候我們應(yīng)該使用GCD定時(shí)器(GCD定時(shí)器是多線(xiàn)程的,不會(huì)和其他線(xiàn)程的runloop 起沖突)
#pragma mark ------ GCD 定時(shí)器 ---------
-(void)GCDTimer {
/** 創(chuàng)建隊(duì)列*/
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//1.創(chuàng)建一個(gè)GCD定時(shí)器(dispatch_source_t本質(zhì)還是個(gè)OC對(duì)象)
/*
第一個(gè)參數(shù):表明創(chuàng)建的是一個(gè)定時(shí)器
*/
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
self.timer = timer;
/** 設(shè)置定時(shí)器的開(kāi)始時(shí)間汰瘫,間隔時(shí)間狂打,精準(zhǔn)度*/
/**
第1個(gè)參數(shù):要給哪個(gè)定時(shí)器設(shè)置
第2個(gè)參數(shù):開(kāi)始時(shí)間
第3個(gè)參數(shù):間隔時(shí)間
第4個(gè)參數(shù):精準(zhǔn)度 一般為0 提高程序的性能
*/
// GCD的時(shí)間參數(shù),一般是納秒(1秒 == 10的9次方納秒)
// 何時(shí)開(kāi)始執(zhí)行第一個(gè)任務(wù)
// dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC) 比當(dāng)前時(shí)間晚3秒
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC));
NSLog(@"%s,line num = %d \n %@",__func__,__LINE__,@"=============分割線(xiàn)");
dispatch_source_set_timer(timer, start, 2.0 * NSEC_PER_SEC, 0);
//下面這個(gè)是時(shí)時(shí)
// dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0);
dispatch_source_set_event_handler(timer, ^{
NSLog(@"%s,line num = %d \n %@",__func__,__LINE__,@"GCDTimer");
});
//4.啟動(dòng)
dispatch_resume(timer);
}