1.基本概念:
什么是進(jìn)程:
1)進(jìn)程是一個(gè)具有獨(dú)立功能的程序關(guān)于某次數(shù)據(jù)集合的一次運(yùn)行活動(dòng)洽胶,他是操作系統(tǒng)分配資源的基本單元.
2) 進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序,就是一段程序執(zhí)行的過程裆馒,我們可以理解為手機(jī)上的一個(gè)app
3)每個(gè)進(jìn)程之間是獨(dú)立的姊氓,每個(gè)進(jìn)程均運(yùn)行在專用且受保護(hù)的內(nèi)存空間內(nèi),擁有獨(dú)立運(yùn)行喷好,所需的全部資源
什么是線程
1)程序執(zhí)行流的最小單元翔横,線程是進(jìn)程中的一個(gè)實(shí)體
2)一個(gè)進(jìn)程要想執(zhí)行任務(wù),必須要至少有一條線程梗搅,應(yīng)用啟動(dòng)的時(shí)候禾唁,系統(tǒng)會默認(rèn)開啟一條線程,也就是主線程无切;
線程和進(jìn)程之間的關(guān)系
1)線程是進(jìn)程的基本執(zhí)行單位荡短,進(jìn)程的所有任務(wù)必須在線程中執(zhí)行(主線程,子線程)
2)線程是cpu分配資源的最小單元哆键,一個(gè)進(jìn)程中所有線程共享進(jìn)程資源
3)一個(gè)進(jìn)程對應(yīng)多個(gè)或者一個(gè)線程掘托,但是必須要有線程
隊(duì)列:這里的隊(duì)列是指執(zhí)行任務(wù)的等待隊(duì)列,即用來存放任務(wù)的隊(duì)列。隊(duì)列是一種特殊的線性表籍嘹,采用FIFO的原則闪盔,即新任務(wù)是被插入到隊(duì)列末尾,讀取總是從頭部開始讀取辱士,讀取一個(gè)釋放一個(gè)
在GCD中有兩種隊(duì)列泪掀,串行和并發(fā),遵循FIFO 區(qū)別在于自行順序不同颂碘,開啟線程數(shù)不同
iOS 中的多線程
NSThread异赫,NSoperationQueue,GCD
我們要明確 NSOperationQueue 與 GCD 之間的關(guān)系
GCD 是面向底層的 C 語言的 API凭涂,NSOpertaionQueue 用 GCD 構(gòu)建封裝的祝辣,是 GCD 的高級抽象。
1切油、GCD 執(zhí)行效率更高蝙斜,而且由于隊(duì)列中執(zhí)行的是由 block 構(gòu)成的任務(wù),這是一個(gè)輕量級的數(shù)據(jù)結(jié)構(gòu)澎胡,寫起 來更方便
2孕荠、GCD 只支持 FIFO 的隊(duì)列娩鹉,而 NSOperationQueue 可以通過設(shè)置最大并發(fā)數(shù),設(shè)置優(yōu)先級稚伍,添加依賴關(guān)系 等調(diào)整執(zhí)行順序
3弯予、NSOperationQueue 甚至可以跨隊(duì)列設(shè)置依賴關(guān)系,但是 GCD 只能通過設(shè)置串行隊(duì)列个曙,或者在隊(duì)列內(nèi)添 加 barrier(dispatch_barrier_async)任務(wù)锈嫩,才能控制執(zhí)行順序,較為復(fù)雜
4、NSOperationQueue 因?yàn)槊嫦驅(qū)ο罂寻幔灾С?KVO呼寸,可以監(jiān)測 operation 是否正在執(zhí)行(isExecuted)、 是否結(jié)束(isFinished)猴贰、是否取消(isCanceld)
? ?實(shí)際項(xiàng)目開發(fā)中对雪,很多時(shí)候只是會用到異步操作,不會有特別復(fù)雜的線程關(guān)系管理米绕,所以蘋果推崇的 且優(yōu)化完善瑟捣、運(yùn)行快速的?GCD?是首選
? ?如果考慮異步操作之間的事務(wù)性,順序行栅干,依賴關(guān)系迈套,比如多線程并發(fā)下載,GCD需要自己寫更多的 代碼來實(shí)現(xiàn)非驮,而?NSOperationQueue?已經(jīng)內(nèi)建了這些支持
? ?不論是GCD還是NSOperationQueue交汤,我們接觸的都是任務(wù)和隊(duì)列,都沒有直接接觸到線程劫笙,事實(shí)上 線程管理也的確不需要我們操心芙扎,系統(tǒng)對于線程的創(chuàng)建,調(diào)度管理和釋放都做得很好填大。而?NSThread?需要我們自己去管理線程的生命周期戒洼,還要考慮線程同步、加鎖問題允华,造成一些性能上的開銷
2圈浇、performSelector
dispatch_async(dispatch_get_global_queue(0, 0), ^{
? ? ? ? [self performSelector:@selector(threadFucntion) withObject:nil afterDelay:0];
? ? });
這里的 threadFucntion 方法是不會去執(zhí)行的,原因在于
這個(gè)方法要?jiǎng)?chuàng)建提交任務(wù)到 runloop 上的靴寂,而 gcd 底層創(chuàng)建的線程是默認(rèn)沒有開啟對應(yīng) runloop 的磷蜀,所有 這個(gè)方法就會失效。
而如果將 dispatch_get_global_queue 改成主隊(duì)列百炬,由于主隊(duì)列所在的主線程是默認(rèn)開啟了 runloop 的褐隆, 就會去執(zhí)行(將 dispatch_async 改成同步,因?yàn)橥绞窃诋?dāng)前線程執(zhí)行剖踊,那么如果當(dāng)前線程是主線程庶弃,test 方法也是會去執(zhí)行的)衫贬。
1、問:怎么用 GCD 實(shí)現(xiàn)多讀單寫?
多讀單寫的意思就是:可以多個(gè)讀者同時(shí)讀取數(shù)據(jù)歇攻,而在讀的時(shí)候固惯,不能去寫入數(shù)據(jù)。并且缴守,在寫的過程 中葬毫,不能有其他寫者去寫。即讀者之間是并發(fā)的屡穗,寫者與讀者或其他寫者是互斥的供常。
這里的寫處理就是通過柵欄的形式去寫。 就可以用?dispatch_barrier_sync(柵欄函數(shù))去實(shí)現(xiàn)
多讀單寫實(shí)現(xiàn)方式:
-(id)readDataForKey:(NSString*)key{
? ? __blockidresult;
? ? //當(dāng)前線程和取的線程為同一個(gè)
? ? dispatch_queue_t concurrent = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
? ? dispatch_sync(concurrent, ^{
? ? ? ? result = [selfvalueForKey:key];
? ? });
? ? returnresult;
}
-(void)writeDataForKey:(NSString*)key{
? ? dispatch_queue_t concurretn = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
? ? dispatch_barrier_async(concurretn, ^{
? ? ? ? [selfsetValue:@"data"forKey:key];
? ? });
}
介紹:dispatch_group_async
場景:在 n 個(gè)耗時(shí)并發(fā)任務(wù)都完成后鸡捐,再去執(zhí)行接下來的任務(wù)。比如麻裁,在 n 個(gè)網(wǎng)絡(luò)請求完成后去刷新 UI 頁 面箍镜。
dispatch_queue_t concurrentQueen = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
? ? dispatch_group_t group = dispatch_group_create();
? ? for(NSIntegeri =0; i <5; i ++ ) {
? ? ? ? dispatch_group_async(group, concurret, ^{
? ? ? ? ? ? sleep(1);
? ? ? ? ? ? NSLog(@"網(wǎng)絡(luò)請求 %ld",i);
? ? ? ? });
? ? }
? ? dispatch_group_notify(group,concurrentQueen, ^{
? ? ? ? NSLog(@"刷新頁面");
? ? });
介紹:Dispatch Semaphore
GCD?中的信號量是指?Dispatch Semaphore,是持有計(jì)數(shù)的信號煎源。
A 常用做色迂,保持線程同步,將異步執(zhí)行任務(wù)轉(zhuǎn)換為同步執(zhí)行
B 保持線程安全手销,為線程加鎖
__blockNSIntegernumber =0;
? ? dispatch_semaphore_t semphone = dispatch_semaphore_create(0);
? ? dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
? ? ? ? number =100;
? ? ? ? sleep(10);
? ? ? ? dispatch_semaphore_signal(semphone);
? ? });
? ? dispatch_semaphore_wait(semphone, DISPATCH_TIME_FOREVER);
? ? NSLog(@"321321aaaaaaa13231? %ld",number);
發(fā)現(xiàn) 過了10秒鐘才打印number = 10 歇僧;
說明阻塞了當(dāng)前線程,將異步執(zhí)行锋拖,轉(zhuǎn)換為同步了
在線程安全中可以將 dispatch_semaphore_wait 看作加鎖诈悍,而 dispatch_semaphore_signal 看作解鎖 首先創(chuàng)建全局變量
@property (nonatomic,strong)dispatch_semaphore_t semphore;
//為線程加鎖
-(void)semPhoreLock{
? ? dispatch_semaphore_wait(_semphore, DISPATCH_TIME_FOREVER);
? ? _count++ ;
? ? sleep(1);
? ? NSLog(@"sdad? d3232 %d",_count);
? ? dispatch_semaphore_signal(_semphore);
}
-(void)asyncDic{
? ? _semphore = dispatch_semaphore_create(1);
? ? for(inti =0; i <100; i ++ ) {
? ? ? ? dispatch_async(dispatch_get_global_queue(0, 0), ^{
? ? ? ? ? ? [selfsemPhoreLock];
? ? ? ? });
? ? }
}
觀察到打印是從1打印100,且dispatch_semaphore_t必須用strong修飾
原因如下:
在子線程中并發(fā)執(zhí)行?asyncTask兽埃,那么第一個(gè)添加到并發(fā)隊(duì)列里的侥钳,會將信號量減?1,此時(shí)信號量等于?0柄错, 可以執(zhí)行接下來的任務(wù)舷夺。而并發(fā)隊(duì)列中其他任務(wù),由于此時(shí)信號量不等于?0售貌,必須等當(dāng)前正在執(zhí)行的任務(wù) 執(zhí)行完畢后調(diào)用?dispatch_semaphore_signal?將信號量加?1给猾,才可以繼續(xù)執(zhí)行接下來的任務(wù),以此類推颂跨,從而 達(dá)到線程加鎖的目的敢伸。
延時(shí)函數(shù)
dispatch_after?
dispatch_after 能讓我們添加進(jìn)隊(duì)列的任務(wù)延時(shí)執(zhí)行,該函數(shù)并不是在指定時(shí)間后執(zhí)行處理毫捣,而只是在指 定時(shí)間追加處理到 dispatch_queue
dispatch_once_tonce 實(shí)現(xiàn)單例
-(instancetype)shareInstance{
? ? static?dispatch_once_tonceToken;
? ? static?id?instance =nil;
? ? dispatch_once(&onceToken, ^{
? ? ? ? instance = [[self?alloc]init];
? ? });
? ? return?instance;
}
NSThread+runloop 實(shí)現(xiàn)常駐線程
調(diào)用performselector 這個(gè)就實(shí)現(xiàn)了打印详拙,說明帝际,實(shí)現(xiàn)NSThread + RunRoop 實(shí)現(xiàn)成功了
自旋鎖:是一種用于保護(hù)多線程共享資源的鎖,與一般互斥鎖(mutex)不同之處在于當(dāng)自旋鎖嘗試獲取鎖時(shí)以忙等 待(busy waiting)的形式不斷地循環(huán)檢查鎖是否可用饶辙。當(dāng)上一個(gè)線程的任務(wù)沒有執(zhí)行完畢的時(shí)候(被鎖住)蹲诀, 那么下一個(gè)線程會一直等待(不會睡眠),當(dāng)上一個(gè)線程的任務(wù)執(zhí)行完畢弃揽,下一個(gè)線程會立即執(zhí)行脯爪。在多 CPU?的環(huán)境中,對持有鎖較短的程序來說矿微,使用自旋鎖代替一般的互斥鎖往往能夠提高程序的性能痕慢。
互斥鎖:當(dāng)上一個(gè)線程的任務(wù)沒有執(zhí)行完畢的時(shí)候(被鎖住),那么下一個(gè)線程會進(jìn)入睡眠狀態(tài)等待任務(wù)執(zhí)行完畢涌矢, 當(dāng)上一個(gè)線程的任務(wù)執(zhí)行完畢掖举,下一個(gè)線程會自動(dòng)喚醒然后執(zhí)行任務(wù)。
自旋鎖會忙等:?所謂忙等娜庇,即在訪問被鎖資源時(shí)塔次,調(diào)用者線程不會休眠,而是不停循環(huán)在那里名秀,直到被鎖 資源釋放鎖励负。互斥鎖會休眠:?所謂休眠匕得,即在訪問被鎖資源時(shí)继榆,調(diào)用者線程會休眠,此時(shí)?cpu?可以調(diào)度其他線程工 作汁掠。直到被鎖資源釋放鎖略吨。此時(shí)會喚醒休眠線程。