iOS 多線程知識梳理
一呕乎、進程和線程
- 1示罗、進程:是一個具有一定獨立功能的程序關(guān)于某次數(shù)據(jù)集合的一次運動活動惩猫,它是操作系統(tǒng)分配資源的基本單元;是指在系統(tǒng)中正在運行的一個程序蚜点,一段程序執(zhí)行的過程轧房,可以理解為手機上的一個app;進程之間是獨立的禽额,每個進程均運行在其專用且受保護的內(nèi)存空間內(nèi)锯厢,擁有獨立運行的全部資源。
- 2脯倒、線程:程序執(zhí)行流的最小單元实辑,線程是進程中的一個實體;一個進程要想執(zhí)行任務(wù)藻丢,必須至少有一條線程剪撬,應(yīng)用程序啟動的時候,系統(tǒng)會默認開啟一條線程悠反,也就是主線程残黑。
- 進程和線程的關(guān)系:線程是進程的執(zhí)行單元,進程的所有任務(wù)都在線程中執(zhí)行斋否;線程是CPU分配資源和調(diào)度的最小單位梨水;一個程序可以對應(yīng)多個進程(多進程),一個進程可以對應(yīng)多個線程茵臭,但至少有一個線程疫诽;同一個進程內(nèi)的線程共享進程資源
二、多進程和多線程
- 1旦委、多進程:進程是程序在計算機上的一次執(zhí)行活動奇徒。當你運行一個程序,你就啟動了一個進程缨硝。顯然摩钙,程序 是死的(靜態(tài)的),進程是活的(動態(tài)的)查辩;進程可以分為系統(tǒng)進程和用戶進程胖笛。凡是用于完成操作系統(tǒng)的各種功能的進程就是系統(tǒng)進程,它們就是處于運行狀態(tài)下的操作系統(tǒng)本身;所有由用戶啟動的進程都是用戶進程宜岛。進程是操作系統(tǒng)進 行資源分配的單位匀钧;進程又被細化為線程,也就是一個進程下有多個能獨立運行的更小的單位谬返。在同一個時間里之斯,同 一個計算機系統(tǒng)中如果允許兩個或兩個以上的進程處于運行狀態(tài),這便是多進程遣铝。
- 2佑刷、多線程:同一時間,CPU只能處理1條線程酿炸,只有1條線程在執(zhí)行瘫絮。多線程并發(fā)執(zhí)行,其實是CPU快速地在多條線程之間調(diào)度(切換)填硕。如果 CPU 調(diào)度線程的時間足夠快麦萤,就造成了多線程并發(fā)執(zhí)行的假象鹿鳖;如果線程非常非常多,CPU會在N多線程之間調(diào)度壮莹,消耗大量的CPU資源翅帜,每條線程被調(diào)度執(zhí)行的頻次 會降低(線程的執(zhí)行效率降低);多線程的優(yōu)點: 能適當提高程序的執(zhí)行效率 能適當提高資源利用率(CPU命满、內(nèi)存利用率)涝滴;多線程的缺點: 開啟線程需要占用一定的內(nèi)存空間(默認情況下,主線程占用 1M胶台,子線程占用 512KB)歼疮,如果開啟大量的 線程,會占用大量的內(nèi)存空間诈唬,降低程序的性能 線程越多韩脏,CPU 在調(diào)度線程上的開銷就越大 程序設(shè)計更加復(fù)雜:比如線程之間的通信、多線程的數(shù)據(jù)共享
三铸磅、任務(wù)和隊列
- 1骤素、任務(wù):就是執(zhí)行操作的意思,也就是在線程中執(zhí)行的那段代碼愚屁。在GCD中是放在block中的济竹。執(zhí)行任務(wù)有兩種方式:同步執(zhí)行(sync)和異步執(zhí)行(async)。
a霎槐、同步(Sync):同步添加任務(wù)到指定的隊列中送浊,在添加的任務(wù)執(zhí)行結(jié)束之前,會一直等待丘跌,直到隊列里面的 任務(wù)完成之后再繼續(xù)執(zhí)行袭景,即會阻塞線程。只能在當前線程中執(zhí)行任務(wù)(是當前線程闭树,不一定是主線程)耸棒, 不具備開啟新線程的能力。
b报辱、異步(Async):線程會立即返回与殃,無需等待就會繼續(xù)執(zhí)行下面的任務(wù),不阻塞當前線程碍现》郏可以在新的線程中 執(zhí)行任務(wù),具備開啟新線程的能力(并不一定開啟新線程)昼接。如果不是添加到主隊列上爽篷,異步會在子線程中 執(zhí)行任務(wù)。 - 2慢睡、隊列:隊列(Dispatch Queue):這里的隊列指執(zhí)行任務(wù)的等待隊列逐工,即用來存放任務(wù)的隊列铡溪。隊列是一種特殊 的線性表,采用 FIFO(先進先出)的原則泪喊,即新任務(wù)總是被插入到隊列的末尾棕硫,而讀取任務(wù)的時候總是從 隊列的頭部開始讀取。每讀取一個任務(wù)窘俺,則從隊列中釋放一個任務(wù) 在 GCD 中有兩種隊列:串行隊列和并發(fā)隊列饲帅。兩者都符合 FIFO(先進先出)的原則复凳。兩者的主要區(qū)別是: 執(zhí)行順序不同瘤泪,以及開啟線程數(shù)不同。
a育八、串行隊列(Serial Dispatch Queue): 同一時間內(nèi)对途,隊列中只能執(zhí)行一個任務(wù),只有當前的任務(wù)執(zhí)行完成之后髓棋,才能執(zhí)行下一個任務(wù)实檀。(只 開啟一個線程,一個任務(wù)執(zhí)行完畢后按声,再執(zhí)行下一個任務(wù))膳犹。主隊列是主線程上的一個串行隊列,是 系統(tǒng)自動為我們創(chuàng)建的。
b签则、并發(fā)隊列(Concurrent Dispatch Queue): 同時允許多個任務(wù)并發(fā)執(zhí)行须床。(可以開啟多個線程,并且同時執(zhí)行任務(wù))渐裂。并發(fā)隊列的并發(fā)功能只有 在異步(dispatch_async)函數(shù)下才有效豺旬。
四、iOS中的多線程:主要有NSThread柒凉、NSOperationQueue和GCD
- 1族阅、NSthread:輕量級別的多線程技術(shù),是我們自己手動開辟的子線程膝捞,如果使用的是初始化方式就需要我們自己啟動坦刀,如果使用的是構(gòu)造器方式 它就會自動啟動。只要是我們手動開辟的線程蔬咬,都需要我們自己管理該線程求泰,不只是啟動,還有該線程使 用完畢后的資源回收计盒。
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(testThread:) object:@"我是參數(shù)"];
//當使用初始化方法出來的主線程需要start啟動
[thread start];
thread.name = @"NSThrea線程";
//調(diào)整線程的優(yōu)先級渴频,取值范圍為:0~1,值越大優(yōu)先級越高北启,先執(zhí)行的概率就會越高卜朗,由于是概率拔第,所以并不能準確的按照設(shè)置的要求執(zhí)行線程任務(wù)
thread.threadPriority = 1;
//取消啟動的線程
[thread cancel];
//通過遍歷構(gòu)造器開辟子線程
[NSThread detachNewThreadSelector:@selector(testThread:) toTarget:self withObject:@"構(gòu)造器方式"];
//performSelector...只要是 NSObject 的子類或者對象都可以通過調(diào)用方法進入子線程和主線程,其實這些 方法所開辟的子線程也是 NSThread 的另一種體現(xiàn)方式场钉。 在編譯階段并不會去檢查方法是否有效存在蚊俺,如果不存在只會給出警告
//在當前線程,延遲1s執(zhí)行逛万,響應(yīng)了OC語言的動態(tài)性泳猬,延遲到運行時才綁定方法
[self performSelector:@selector(aaa) withObject:nil afterDelay:1];
//回到主線程:是否將該回調(diào)方法執(zhí)行完再執(zhí)行后面的代碼,如果為YES宇植;就必須等回調(diào)方法執(zhí)行完成后才能執(zhí)行后面的代碼
[self performSelectorOnMainThread:@selector(aaa) withObject:nil withUntilDone:Yes];
//開辟子線程
[self performSelectorInBackground:@selector(aaa) withObject:nil];
//在指定線程執(zhí)行
[self performSelector:@selector(aaa) onThread:[NSThread currentThread] withObject:nil waitUntilDone:Yes];
//需要注意的是:如果是帶 afterDelay 的延時函數(shù)得封,會在內(nèi)部創(chuàng)建一個 NSTimer,然后添加到當前線程的 Runloop 中指郁。也就是如果當前線程沒有開啟 runloop忙上,該方法會失效。在子線程中闲坎,需要啟動 runloop(注 意調(diào)用順序)
[self performSelector:@selector(aaa) withObject:nil afterDelay:1];
[[NSRunLoop currentRunLoop] run];
//而 performSelector:withObject:只是一個單純的消息發(fā)送疫粥,和時間沒有一點關(guān)系。所以不需要添加到子 線程的 Runloop 中也能執(zhí)行
- 2腰懂、GCD 對比 NSOprationQueue
我們要明確 NSOperationQueue 與 GCD 之間的關(guān)系;
GCD 是面向底層的C語言的 API梗逮,NSOpertaionQueue 用 GCD 構(gòu)建封裝的,是 GCD 的高級抽象绣溜。
1慷彤、GCD 執(zhí)行效率更高瞬欧,而且由于隊列中執(zhí)行的是由 block 構(gòu)成的任務(wù)艘虎,這是一個輕量級的數(shù)據(jù)結(jié)構(gòu),寫起來更方便;
2、GCD只支持FIFO的隊列唯鸭,而NSOperationQueue可以通過設(shè)置最大并發(fā)數(shù)明肮,設(shè)置優(yōu)先級柿估,添加依賴關(guān)系等調(diào)整執(zhí)行順序;
3、NSOperationQueue甚至可以跨隊列設(shè)置依賴關(guān)系钠右,但是GCD只能通過設(shè)置串行隊列搁凸,或者在隊列內(nèi)添加barrier(dispatch_barrier_async)任務(wù)褥芒,才能控制執(zhí)行順序,較為復(fù)雜;
4、NSOperationQueue因為面向?qū)ο缶┤颍灾С諯VO讯蒲,可以監(jiān)測 operation是否正在執(zhí)行(isExecuted)赁酝、是否結(jié)束(isFinished)雷则、是否取消(isCanceld)
實例實際說明:
a月劈、實際項目開發(fā)中,很多時候只是會用到異步操作,不會有特別復(fù)雜的線程關(guān)系管理拴念,所以蘋果推崇的 且優(yōu)化完善队魏、運行快速的GCD是首選官帘;
b、如果考慮異步操作之間的事務(wù)性,順序行,依賴關(guān)系稍刀,比如多線程并發(fā)下載,GCD需要自己寫更多的代碼來實現(xiàn),而NSOperationQueue已經(jīng)內(nèi)建了這些支持;
c萄喳、不論是GCD還是 NSOperationQueue减江,我們接觸的都是任務(wù)和隊列您市,都沒有直接接觸到線程手蝎,事實上線程管理也的確不需要我們操心,系統(tǒng)對于線程的創(chuàng)建,調(diào)度管理和釋放都做得很好。而 NSThread需要我們自己去管理線程的生命周期,還要考慮線程同步、加鎖問題,造成一些性能上的開銷
- 3、GCD-隊列
iOS中,有GCD灵寺、NSOperation、NSThread等幾種多線程技術(shù)方案瓤檐。
而GCD共有三種隊列類型:
main queue:通過 dispatch_get_main_queue()獲得谴古,這是一個與主線程相關(guān)的串行隊列恩敌。
global queue:全局隊列是并發(fā)隊列,由整個進程共享。存在著高月趟、中灯蝴、低三種優(yōu)先級的全局隊列。
調(diào)用 dispath_get_global_queue 并傳入優(yōu)先級來訪問隊列孝宗。
自定義隊列:通過函數(shù) dispatch_queue_create 創(chuàng)建的隊列
五穷躁、死鎖:死鎖就是隊列引起的循環(huán)等待
- 1、一個比較常見的死鎖例子:主隊列同步
- (void)viewDidLoad{
[super viewDidLoad];
dispatch_sync(dispatch_get_main_queue(),^{
NSLog(@"deadlock");
});
}
在主線程中運用主隊列同步因妇,也就是把任務(wù)放到了主線程的隊列中问潭。
同步對于任務(wù)是立刻執(zhí)行的,那么當把任務(wù)放進主隊列時婚被,它就會立馬執(zhí)行,只有執(zhí)行完這個任務(wù)狡忙,viewDidLoad才會繼續(xù)向下執(zhí)行。
而 viewDidLoad 和任務(wù)都是在主隊列上的址芯,由于隊列的先進先出原則灾茁,
任務(wù)又需等待viewDidLoad執(zhí)行完 畢后才能繼續(xù)執(zhí)行,
viewDidLoad 和這個任務(wù)就形成了相互循環(huán)等待谷炸,就造成了死鎖北专。
想避免這種死鎖,可以將同步改成異步dispatch_async,
或者將dispatch_get_main_queue換成其他串行或并行隊列旬陡,都可以解決拓颓。
- 2、同樣描孟,下邊的代碼也會造成死鎖
dispatch_queue_t serialQueue = dispatch_queue_create("test",DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue,^{
dispatch_sync(serialQueue,^{
NSLog(@"deadlock");
});
});
外面的函數(shù)無論是同步還是異步都會造成死鎖驶睦。
這是因為里面的任務(wù)和外面的任務(wù)都在同一個 serialQueue 隊列內(nèi),又是同步画拾,這就和上邊主隊列同步的例子一樣造成了死鎖;
解決方法也和上邊一樣啥繁,將里面的同步改成異步dispatch_async,
或者將 serialQueue 換成其他串行或并 行隊列,都可以解決
dispatch_queue_t serialQueue = dispatch_queue_create("test",DISPATCH_QUEUE_SERIAL);
dispatch_queue_t serialQueue2 = dispatch_queue_create("test2",DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue,^{
dispatch_sync(serialQueue2,^{
NSLog(@"deadlock");
});
});
這樣是不會死鎖的,并且 serialQueue和serialQueue2是在同一個線程中的青抛。
六、GCD任務(wù)執(zhí)行順序
- 1酬核、串行隊列先異步后同步
dispatch_queue_t serialQueue = diapatch_queue_create("test",DISPATCH_QUEUE_SERIAL);
NSLog(@"1");
dispathc_async(serialQueue,^{
NSLog(@"2");
});
NSLog(@"3");
dispathc_sync(serialQueue,^{
NSLog(@"4");
});
NSLog(@"5");
打印順序是13245
原因是: 首先先打印 1 接下來將任務(wù) 2 其添加至串行隊列上蜜另,
由于任務(wù) 2 是異步,不會阻塞線程嫡意,繼續(xù)向下執(zhí)行举瑰,
打印 3 然后是任務(wù) 4,將任務(wù) 4 添加至串行隊列上,因為任務(wù) 4 和任務(wù) 2 在同一串行隊列蔬螟,根據(jù)隊列先進先出原則此迅,
任務(wù) 4 必須等任務(wù) 2 執(zhí)行后才能執(zhí)行,又因為任務(wù) 4 是同步任務(wù),
會阻塞線程耸序,只有執(zhí)行完任務(wù) 4 才能繼 續(xù)向下執(zhí)行打印 5 所以最終順序就是 13245忍些。 這里的任務(wù) 4 在主線程中執(zhí)行,
而任務(wù) 2 在子線程中執(zhí)行坎怪。 如果任務(wù) 4 是添加到另一個串行隊列或者并行隊列罢坝,
則任務(wù) 2 和任務(wù) 4 無序執(zhí)行(可以添加多個任務(wù)看效果)
- 2、performSelector
dispatch_async(dispatch_get_global_queue(0,0),^{
[self performSelector:@selector(test:) withObject:nil afterDelay:0];
});
這里的 test 方法是不會去執(zhí)行的搅窿,原因在于
這個方法要創(chuàng)建提交任務(wù)到 runloop 上的嘁酿,而 gcd 底層創(chuàng)建的線程是默認沒有開啟對應(yīng) runloop 的,
所有 這個方法就會失效男应。 而如果將 dispatch_get_global_queue 改成主隊列闹司,
由于主隊列所在的主線程是默認開啟了 runloop 的,就會去執(zhí)行(將 dispatch_async 改成同步沐飘,
因為同步是在當前線程執(zhí)行开仰,那么如果當前線程是主線程,
test 方法也是會去執(zhí)行的)薪铜。
七众弓、dispatch_barrier_async
- 1、怎么用 GCD 實現(xiàn)多讀單寫隔箍?
多讀單寫的意思就是:可以多個讀者同時讀取數(shù)據(jù)谓娃,而在讀的時候,不能取寫入數(shù)據(jù)蜒滩。
并且滨达,在寫的過程 中,不能有其他寫者去寫俯艰。
即讀者之間是并發(fā)的捡遍,寫者與讀者或其他寫者是互斥的。
這里的寫處理就是通過柵欄的形式去寫竹握。
就可以用dispatch_barrier_sync(柵欄函數(shù))去實現(xiàn)
- 2画株、dispatch_barrier_sync 的用法
dispatch_queue_t concurrentQueue = diapatch_queue_create("test",DISPATCH_QUEUE_CONCURRENT);
for(NSInteger i = 0; i < 10; i++){
dispatch_async(concurrentQueue,^{
NSLog(@"zd",i);
});
}
dispatch_barrier_sync(concurrentQueue,^{
NSLog(@"barrier");
})
for(NSInteger i = 10; i < 20; i++){
dispatch_async(concurrentQueue,^{
NSLog(@"zd",i);
});
}
這里的 dispatch_barrier_sync 上的隊列要和需要阻塞的任務(wù)在同一隊列上,否則是無效的啦辐。
從打印上看谓传,任務(wù) 0-9 和任務(wù)任務(wù) 10-19 因為是異步并發(fā)的原因,彼此是無序的芹关。
而由于柵欄函數(shù)的存在续挟,導(dǎo)致順序必然是先執(zhí)行任務(wù) 0-9,再執(zhí)行柵欄函數(shù)侥衬,再去執(zhí)行任務(wù) 10-19诗祸。
dispatch_barrier_sync: Submits a barrier block object for execution and waits until that block completes.(提交一個柵欄函數(shù)在執(zhí)行中,它會等待柵欄函數(shù)執(zhí)行完)
dispatch_barrier_async: Submits a barrier block for asynchronous execution and returns immediately.(提交一個柵欄函數(shù)在異步執(zhí)行中,它會立馬返回)
而 dispatch_barrier_sync 和 dispatch_barrier_async 的區(qū)別也就在于會不會阻塞當前線程
比如跑芳,上述代碼如果在 dispatch_barrier_async 后隨便加一條打印,則會先去執(zhí)行該打印直颅,
再去執(zhí) 行任務(wù) 0-9 和柵欄函數(shù)博个;而如果是 dispatch_barrier_sync,則會在任務(wù) 0-9 和柵欄函數(shù)后去執(zhí)行這 條打印际乘。
- 3坡倔、設(shè)計多讀單寫
- (id)readDataForKey:(NSString *)key{
__block id result;
dispatch_sync(_currentQueue,^{
result = [self valueForKey:key];
});
return result;
}
- (void)writeData:(id)data forKey:(NSString *)key{
dispatch_async(_currentQueue,^{
[self setValue:data forKey:key];
});
}
八、dispatch_group_async和dispatch_semaphore
- 1脖含、dispatch_group_async場景:在 n 個耗時并發(fā)任務(wù)都完成后罪塔,再去執(zhí)行接下來的任務(wù)。比如养葵,在 n 個網(wǎng)絡(luò)請求完成后去刷新 UI 頁 面征堪。
dispatch_queue_t concurrentQueue = dispatch_queue_create_("test1",DISPATCH_QUEUE_CONCURRENT);
dispathc_group_t group = dispatch_group_create();
for(NSInteger i = 0; i < 10; i++){
dispatch_group_async(group,concurrentQueue,^{
sleep(1);
NSLog(@%zd:網(wǎng)絡(luò)請求",i);
});
}
dispatch_group_notify(group,dispatch_get_main_queue,^{
NSLog("刷新頁面");
});
- 2、dispatch_semaphore
GCD中的信號量是指Dispatch Semaphore关拒,是持有計數(shù)的信號;
Dispatch Semaphore 提供了三個函數(shù):
1.dispatch_semaphore_create:創(chuàng)建一個 Semaphore 并初始化信號的總量
2.dispatch_semaphore_signal:發(fā)送一個信號佃蚜,讓信號總量加 1 3.dispatch_semaphore_wait:可以使總信號量減 1,當信號總量為 0 時就會一直等待(阻塞所在線程)着绊,否則就可以正常執(zhí)行谐算。
Dispatch Semaphore 在實際開發(fā)中主要用于:
1、保持線程同步归露,將異步執(zhí)行任務(wù)轉(zhuǎn)換為同步執(zhí)行任務(wù)
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block NSInteger number = 0;
dispatch_async(dispatch_get_global_queue(0,0),^{
number = 100;
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore,DISPATH_TIME_FOREVER);
NSLog(@"semaphore--end,number = %zd",number);
//dispatch_semaphore_wait 加鎖阻塞了當前線程洲脂,dispatch_semaphore_signal 解鎖后當前線程繼續(xù)執(zhí)行
2、保證線程安全剧包,為線程加鎖
在線程安全中可以將 dispatch_semaphore_wait 看作加鎖恐锦,而 dispatch_semaphore_signal 看作解鎖 首先創(chuàng)建全局變量
_semaphore = dispatch_semaphore_create(1);
注意到這里的初始化信號量是 1。
- (void)asyncTask{
dispatch_semaphore_wait(_semaphore,DISPATH_TIME_FOREVER);
count++;
sleep(1);
NSLog(@"執(zhí)行任務(wù)",count);
dispatch_semaphore_signal(_semaphore);
}
異步并發(fā)調(diào)用 asyncTask
for(NSInteger i = 0; i < 100; i++){
dispatch_async(dispatch_get_global_queue(0,0),^{
[self asyncTask];
});
}
然后發(fā)現(xiàn)打印是從任務(wù) 1 順序執(zhí)行到 100疆液,沒有發(fā)生兩個任務(wù)同時執(zhí)行的情況一铅。
原因如下: 在子線程中并發(fā)執(zhí)行 asyncTask,那么第一個添加到并發(fā)隊列里的堕油,
會將信號量減 1潘飘,此時信號量等于 0,可以執(zhí)行接下來的任務(wù)馍迄。而并發(fā)隊列中其他任務(wù)福也,由于此時信號量不等于 0,
必須等當前正在執(zhí)行的任務(wù) 執(zhí)行完畢后調(diào)用 dispatch_semaphore_signal 將信號量加 1攀圈,才可以繼續(xù)執(zhí)行接下來的任務(wù),以此類推峦甩,從而 達到線程加鎖的目的赘来。
九现喳、延時函數(shù)dispatch_after和dispatch_once
-
1、dispatch_after 能讓我們添加進隊列的任務(wù)延時執(zhí)行犬辰,該函數(shù)并不是在指定時間后執(zhí)行處理嗦篱,而只是在指 定時間追加處理到 dispatch_queue
dispathc_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2 * NSEC_PER_SEC)),dispatch_get_main_queue(),^{ NSLog(@""diaptch_after); }); 由于其內(nèi)部使用的是 dispatch_time_t 管理時間, 而不是 NSTimer幌缝。 所以如果在子線程中調(diào)用灸促,相比 performSelector:afterDelay, 不用關(guān)心 runloop 是否開啟
2、dispatch_once創(chuàng)建單例
+ (instancetype)shareInstance{
static dispatch_onece_t onceToken;
static id stance = nil;
dispatch_once(&onceToken,^{
instance = [[self alloc] init];
});
return instance;
}
十涵卵、NSOperationQueue 的優(yōu)點
- 1浴栽、NSOperation、NSOperationQueue 是蘋果提供給我們的一套多線程解決方案轿偎。實際上 NSOperation典鸡、 NSOperationQueue 是基于 GCD 更高一層的封裝,完全面向?qū)ο蠡祷蕖5潜?GCD 更簡單易用萝玷、代碼可讀性 也更高。
- 2昆婿、可以添加任務(wù)依賴球碉,方便控制執(zhí)行順序
- 3、可以設(shè)定操作執(zhí)行的優(yōu)先級
- 4仓蛆、任務(wù)執(zhí)行狀態(tài)控制:isReady,isExecuting,isFinished,isCancelled睁冬;如果只是重寫 NSOperation 的 main 方法,由底層控制變更任務(wù)執(zhí)行及完成狀態(tài)多律,以及任務(wù)退出 如果重寫了 NSOperation 的 start 方法痴突,自行控制任務(wù)狀態(tài) 系統(tǒng)通過 KVO 的方式移除 isFinished==YES 的 NSOperation
- 5、可以設(shè)置最大并發(fā)量
十一狼荞、NSOperation 和 NSOperationQueue
- 1辽装、操作Operation:執(zhí)行操作的意思,換句話說就是你在線程中執(zhí)行的那段代碼相味。 在 GCD 中是放在 block 中的拾积。在 NSOperation 中,使用 NSOperation 子類 NSInvocationOperation丰涉、 NSBlockOperation拓巧,或者自定義子類來封裝操作。
- 2一死、操作隊列Operation Queues:這里的隊列指操作隊列肛度,即用來存放操作的隊列。不同于 GCD 中的調(diào)度隊列 FIFO(先進先出)的原則投慈。 NSOperationQueue 對于添加到隊列中的操作承耿,首先進入準備就緒的狀態(tài)(就緒狀態(tài)取決于操作之間的依賴 關(guān)系)冠骄,然后進入就緒狀態(tài)的操作的開始執(zhí)行順序(非結(jié)束執(zhí)行順序)由操作之間相對的優(yōu)先級決定(優(yōu) 先級是操作對象自身的屬性)。操作隊列通過設(shè)置最大并發(fā)操作數(shù)(maxConcurrentOperationCount)來控制并發(fā)加袋、串行凛辣。 NSOperationQueue 為我們提供了兩種不同類型的隊列:主隊列和自定義隊列。主隊列運行在主線程之上职烧, 而自定義隊列在后臺執(zhí)行扁誓。
十二、
- 1蚀之、NSThread 在實際開發(fā)中比較常用到的場景就是去實現(xiàn)常駐線程蝗敢。由于每次開辟子線程都會消耗 cpu,在需要頻繁使用子線程的情況下恬总,頻繁開辟子線程會消耗大量的 cpu前普,而且創(chuàng)建線程都是任務(wù)執(zhí)行完成之后也就釋放了,不能再次利用壹堰,那么如何創(chuàng)建一個線程可以 讓它可以再次工作呢拭卿?也就是創(chuàng)建一個常駐線程。 首先常駐線程既然是常駐贱纠,那么我們可以用 GCD 實現(xiàn)一個單例來保存 NSThread
+ (NSThread *)sharedThread{
static NSThread *shareThread = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
shareThread = [[NSThread alloc] initWithTarget:self selector:@selector(threadTest) object:nil];
[shareThread setName:@"threadtest"];
[shareThread start];
});
}
return shareThread;
+ (void)threadTest{
@autoreleasepool{
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
[runLoop run];
}
}
- 2峻厚、
十三、自旋鎖與互斥鎖
- 1谆焊、自旋鎖: 是一種用于保護多線程共享資源的鎖惠桃,與一般互斥鎖(mutex)不同之處在于當自旋鎖嘗試獲取鎖時以忙等 待(busy waiting)的形式不斷地循環(huán)檢查鎖是否可用。當上一個線程的任務(wù)沒有執(zhí)行完畢的時候(被鎖紫绞浴)辜王, 那么下一個線程會一直等待(不會睡眠),當上一個線程的任務(wù)執(zhí)行完畢罐孝,下一個線程會立即執(zhí)行呐馆。 在多 CPU 的環(huán)境中,對持有鎖較短的程序來說莲兢,使用自旋鎖代替一般的互斥鎖往往能夠提高程序的性能汹来。
- 2、互斥鎖: 當上一個線程的任務(wù)沒有執(zhí)行完畢的時候(被鎖赘耐А)收班,那么下一個線程會進入睡眠狀態(tài)等待任務(wù)執(zhí)行完畢, 當上一個線程的任務(wù)執(zhí)行完畢谒兄,下一個線程會自動喚醒然后執(zhí)行任務(wù)眠寿。
- 3览绿、總結(jié): 自旋鎖會忙等: 所謂忙等,即在訪問被鎖資源時,調(diào)用者線程不會休眠豪筝,而是不停循環(huán)在那里,直到被鎖 資源釋放鎖舔琅。 互斥鎖會休眠: 所謂休眠碗硬,即在訪問被鎖資源時,調(diào)用者線程會休眠绵咱,此時 cpu 可以調(diào)度其他線程工 作碘饼。直到被鎖資源釋放鎖。此時會喚醒休眠線程悲伶。
- 4艾恼、優(yōu)缺點:
自旋鎖的優(yōu)點在于,因為自旋鎖不會引起調(diào)用者睡眠麸锉,所以不會進行線程調(diào)度钠绍,CPU 時間片輪轉(zhuǎn)等耗時操 作。所有如果能在很短的時間內(nèi)獲得鎖花沉,自旋鎖的效率遠高于互斥鎖柳爽。 缺點在于,自旋鎖一直占用 CPU碱屁,他在未獲得鎖的情況下磷脯,一直運行--自旋,所以占用著 CPU娩脾,如果不 能在很短的時 間內(nèi)獲得鎖赵誓,這無疑會使 CPU 效率降低。自旋鎖不能實現(xiàn)遞歸調(diào)用柿赊。 - 5俩功、自旋鎖:atomic、OSSpinLock碰声、dispatch_semaphore_t
- 6诡蜓、互斥鎖:pthread_mutex、@ synchronized奥邮、NSLock万牺、NSConditionLock 、NSCondition洽腺、NSRecursiveLock