線程和進(jìn)程:
線程是指進(jìn)程中的一個(gè)執(zhí)行流程,程序執(zhí)行流的最小單元,一個(gè)進(jìn)程要想執(zhí)行任務(wù),必須至少有一條線程.應(yīng)用程序啟動的時(shí)候,系統(tǒng)會默認(rèn)開啟一條線程,也就是主線程
進(jìn)程是一個(gè)內(nèi)存中運(yùn)行的應(yīng)用程序,一個(gè)具有一定獨(dú)立功能的程序關(guān)于某次數(shù)據(jù)集合的一次運(yùn)行活動,它是操作系統(tǒng)分配資源的基本單元;每個(gè)進(jìn)程之間是獨(dú)立的,每個(gè)進(jìn)程均運(yùn)行在其專用且受保護(hù)的內(nèi)存空間內(nèi),擁有獨(dú)立運(yùn)行所需的全部資源
進(jìn)程和線程的關(guān)系
1.線程是進(jìn)程的執(zhí)行單元,進(jìn)程的所有任務(wù)都在線程中執(zhí)行
2.線程是 CPU 分配資源和調(diào)度的最小單位
3.一個(gè)程序可以對應(yīng)多個(gè)進(jìn)程(多進(jìn)程),一個(gè)進(jìn)程中可有多個(gè)線程,但至少要有一條線程
4.同一個(gè)進(jìn)程內(nèi)的線程共享進(jìn)程資源
多線程的實(shí)現(xiàn)原理:事實(shí)上,同一時(shí)間內(nèi)單核的CPU只能執(zhí)行一個(gè)線程,多線程是CPU快速的在多個(gè)線程之間進(jìn)行切換(調(diào)度),造成了多個(gè)線程同時(shí)執(zhí)行的假象。
優(yōu)點(diǎn):
能適當(dāng)提高程序的執(zhí)行效率
能適當(dāng)提高資源利用率(CPU、內(nèi)存利用率)
缺點(diǎn):
開啟線程需要占用一定的內(nèi)存空間(默認(rèn)情況下,主線程占用1M,子線程占用512KB),如果開啟大量的線程,會占用大量的內(nèi)存空間,降低程序的性能
線程越多,CPU在調(diào)度線程上的開銷就越大
NSThread
NSThread是封裝程度最小最輕量級的,使用更靈活,但要手動管理線程的生命周期、線程同步和線程加鎖等,開銷較大;
/* 1 是否開啟了多線程 */
BOOL isMultiThreaded = [NSThread isMultiThreaded];
/* 2 獲取當(dāng)前線程 */
NSThread *currentThread = [NSThread currentThread];
?/* 3 獲取主線程 */
NSThread *mainThread = [NSThread mainThread];
/* 1 線程實(shí)例對象創(chuàng)建與設(shè)置 */
NSThread *newThread= [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
/* 設(shè)置線程優(yōu)先級threadPriority(0~1.0),即將被拋棄,將使用qualityOfService代替 */
newThread.threadPriority = 1.0;
newThread.qualityOfService = NSQualityOfServiceUserInteractive;
?/* 開啟線程 */
?[newThread start];
啟動流程:
start() ——> 創(chuàng)建pthread?——> main()??——> [target performSelector:]?——>? exit()
/* 2 靜態(tài)方法快速創(chuàng)建并開啟新線程 */
? ? [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
? ? [NSThread detachNewThreadWithBlock:^{
? ? ? ? NSLog(@"block run...");
? ? }];
/** NSObejct基類隱式創(chuàng)建線程的一些靜態(tài)工具方法 **/
? ? /* 1 在當(dāng)前線程上執(zhí)行方法,延遲2s */
? ? [self performSelector:@selector(run) withObject:nil afterDelay:2.0];
? ? /* 2 在指定線程上執(zhí)行方法,不等待當(dāng)前線程 */
? ? [self performSelector:@selector(run) onThread:newThread withObject:nil waitUntilDone:NO];
? ? /* 3 后臺異步執(zhí)行函數(shù) */
? ? [self performSelectorInBackground:@selector(run) withObject:nil];
? ? /* 4 在主線程上執(zhí)行函數(shù) */
? ? [self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:NO];
GCD??
https://juejin.im/post/58fd55c08d6d81005898df46#heading-5
GCD(Grand Central Dispatch),又叫大中央調(diào)度,對線程操作進(jìn)行了封裝,加入了很多新的特性,內(nèi)部進(jìn)行了效率優(yōu)化,提供了簡潔的C語言接口,使用更加簡單高效,也是蘋果推薦的方式
同步dispatch_sync與異步dispatch_async任務(wù)派發(fā)
隊(duì)列
Dispatch Queue是執(zhí)行處理的等待隊(duì)列,按照任務(wù)(block)追加到隊(duì)列里的順序,先進(jìn)先出執(zhí)行處理。
而等待隊(duì)列有兩種
Serial Dispatch Queue:串行隊(duì)列,等待當(dāng)前執(zhí)行任務(wù)處理結(jié)束的隊(duì)列。
Concurrent Dispatch Queue:并發(fā)隊(duì)列,不等待當(dāng)前執(zhí)行任務(wù)處理結(jié)束的隊(duì)列。
dispatch_once_t只執(zhí)行一次
dispatch_after延后執(zhí)行
串行指的是隊(duì)列內(nèi)任務(wù)一個(gè)接一個(gè)的執(zhí)行,任務(wù)之間要依次等待不可重合,且添加的任務(wù)按照先進(jìn)先出FIFO的順序執(zhí)行
并行指的是同一個(gè)隊(duì)列先后添加的多個(gè)任務(wù)可以同時(shí)并列執(zhí)行,任務(wù)之間不會相互等待,且這些任務(wù)的執(zhí)行順序執(zhí)行過程不可預(yù)測
GCD多線程編程時(shí)經(jīng)常會使用dispatch_async和dispatch_sync函數(shù)往指定隊(duì)列中添加任務(wù)塊,區(qū)別就是同步和異步。同步指的是阻塞當(dāng)前線程,要等添加的耗時(shí)任務(wù)塊block完成后,函數(shù)才能返回,后面的代碼才可以繼續(xù)執(zhí)行。如果在主線上,則會發(fā)生阻塞,用戶會感覺應(yīng)用不響應(yīng),這是要避免的。
/* 1. 提交異步任務(wù) */
NSLog(@"開始提交異步任務(wù):");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
? ? /* 耗時(shí)任務(wù)... */
? ? [NSThread sleepForTimeInterval:10];
});
NSLog(@"異步任務(wù)提交成功!");
/* 2. 提交同步任務(wù) */
NSLog(@"開始提交同步任務(wù):");
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
? ? /* 耗時(shí)任務(wù)... */
? ? [NSThread sleepForTimeInterval:10];
});
NSLog(@"同步任務(wù)提交成功!");
/* 創(chuàng)建一個(gè)并發(fā)隊(duì)列 */
dispatch_queue_t concurrent_queue = dispatch_queue_create("demo.gcd.concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
/* 創(chuàng)建一個(gè)串行隊(duì)列 */
dispatch_queue_t serial_queue = dispatch_queue_create("demo.gcd.serial_queue", DISPATCH_QUEUE_SERIAL);
dispatch_once_t 這個(gè)函數(shù)控制指定代碼只會被執(zhí)行一次,常用來實(shí)現(xiàn)單例模式,這里以單例模式實(shí)現(xiàn)的模板代碼為例展示dispatch_once_t的用法,其中的實(shí)例化語句只會被執(zhí)行一次:
+ (instancetype *)sharedInstance {
? ? static dispatch_once_t once = 0;
? ? static id sharedInstance = nil;
? ? dispatch_once(&once, ^{
? ? ? ? // 只實(shí)例化一次
? ? ? ? sharedInstance = [[self alloc] init];
? ? });
? ? return sharedInstance;
}
dispatch_after 通過該函數(shù)可以讓要提交的任務(wù)在從提交開始后的指定時(shí)間后執(zhí)行,也就是定時(shí)延遲執(zhí)行提交的任務(wù),使用方法很簡單:
// 定義延遲時(shí)間:3s
? ? dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC));
? ? dispatch_after(delay, dispatch_get_main_queue(), ^{
? ? ? ? // 要執(zhí)行的任務(wù)...
? ? });
dispatch_group_t
組調(diào)度可以實(shí)現(xiàn)等待一組操作都完成后執(zhí)行后續(xù)操作,典型的例子是大圖片的下載,例如可以將大圖片分成幾塊同時(shí)下載,等各部分都下載完后再后續(xù)將圖片拼接起來,提高下載的效率。使用方法如下:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{ /*操作1 */ });
dispatch_group_async(group, queue, ^{ /*操作2 */ });
dispatch_group_async(group, queue, ^{ /*操作3 */ });
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
? ? ? ? // 后續(xù)操作... });
dispatch_ barrier_async:柵欄 解決多讀單寫問題
GCD 信號量:dispatch_semaphore :
Dispatch Semaphore提供了三個(gè)方法:
dispatch_semaphore_create:創(chuàng)建一個(gè) Semaphore 并初始化信號的總量
dispatch_semaphore_signal:發(fā)送一個(gè)信號,讓信號總量加 1
dispatch_semaphore_wait:可以使總信號量減 1,信號總量小于 0 時(shí)就會一直等待(阻塞所在線程),否則就可以正常執(zhí)行。
Dispatch Semaphore 在實(shí)際開發(fā)中主要用于:
保持線程同步,將異步執(zhí)行任務(wù)轉(zhuǎn)換為同步執(zhí)行任務(wù)
保證線程安全,為線程加鎖
NSOperation
NSOperation是基于GCD的一個(gè)抽象基類,將線程封裝成要執(zhí)行的操作,不需要管理線程的生命周期和同步,但比GCD可控性更強(qiáng),例如可以加入操作依賴(addDependency)、設(shè)置操作隊(duì)列最大可并發(fā)執(zhí)行的操作個(gè)數(shù)(setMaxConcurrentOperationCount)、取消操作(cancel)任務(wù)狀態(tài)的控制等等
NSOperation作為抽象基類不具備封裝我們的操作的功能,需要使用兩個(gè)它的實(shí)體子類:NSBlockOperation和NSInvocationOperation,或者繼承NSOperation自定義子類。
NSInvocationOperation *invoOpertion = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
? ? [invoOpertion start];
? ? /* NSBlockOperation初始化 */
? ? NSBlockOperation *blkOperation = [NSBlockOperation blockOperationWithBlock:^{
? ? ? ? NSLog(@"NSBlockOperation");
? ? }];
? ? [blkOperation start];
另外NSBlockOperation可以后續(xù)繼續(xù)添加block執(zhí)行塊,操作啟動后會在不同線程并發(fā)的執(zhí)行這些執(zhí)行快:
/* NSBlockOperation初始化 */
NSBlockOperation *blkOperation = [NSBlockOperation blockOperationWithBlock:^{
? ? NSLog(@"NSBlockOperationA");
}];
[blkOperation addExecutionBlock:^{
? ? NSLog(@"NSBlockOperationB");
}];
[blkOperation addExecutionBlock:^{
? ? NSLog(@"NSBlockOperationC");
}];
[blkOperation start];
另外說了NSOperation的可控性比GCD要強(qiáng),其中一個(gè)非常重要的特性是可以設(shè)置各操作之間的依賴,即強(qiáng)行規(guī)定操作A要在操作B完成之后才能開始執(zhí)行,成為操作A依賴于操作B:
/* 獲取主隊(duì)列(主線程) */
NSOperationQueue *queue = [NSOperationQueue mainQueue];
/* 創(chuàng)建a、b、c操作 */
NSOperation *c = [NSBlockOperation blockOperationWithBlock:^{
? ? NSLog(@"OperationC");
}];
NSOperation *a = [NSBlockOperation blockOperationWithBlock:^{
? ? NSLog(@"OperationA");
}];
NSOperation *b = [NSBlockOperation blockOperationWithBlock:^{
? ? NSLog(@"OperationB");
}];
/* 添加操作依賴,c依賴于a和b,這樣c一定會在a和b完成后才執(zhí)行,即順序?yàn)椋篈、B、C */
[c addDependency:a];
[c addDependency:b];
/* 添加操作a、b、c到操作隊(duì)列queue(特意將c在a和b之前添加) */
[queue addOperation:c];
[queue addOperation:a];
[queue addOperation:b];
任務(wù)狀態(tài)控制:
自定義任務(wù)狀態(tài)控制:
如果只重寫了main()方法,底層控制變更任務(wù)執(zhí)行完成狀態(tài)以及任務(wù)退出
如果重寫了start方法, 自行控制任務(wù)狀態(tài)
isReady
isExecuting
isFinished
isCancelled
多線程在iOS開發(fā)中的應(yīng)用 :
顯示\刷新UI界面
處理UI事件(比如點(diǎn)擊事件、滾動事件、拖拽事件等)
別將比較耗時(shí)的操作放到主線程中
耗時(shí)操作會卡住主線程,嚴(yán)重影響UI的流暢度,給用戶一種“卡”的壞體驗(yàn)
線程安全:
鎖是最常用的同步工具。一段代碼段在同一個(gè)時(shí)間只能允許被一個(gè)線程訪問
13種加鎖方案:
(1)OSSpinLock自旋鎖??
首先要提的是OSSpinLock已經(jīng)出現(xiàn)了BUG,導(dǎo)致并不能完全保證是線程安全的。
(2) 條件信號量dispatch_semaphore_t
dispatch_semaphore_t GCD中信號量,也可以解決資源搶占問題,支持信號通知和信號等待。每當(dāng)發(fā)送一個(gè)信號通知,則信號量+1;每當(dāng)發(fā)送一個(gè)等待信號時(shí)信號量-1,;如果信號量為0則信號會處于等待狀態(tài),直到信號量大于0開始執(zhí)行。
dispatch_semaphore_t semaphore;
* 創(chuàng)建一個(gè)信號量為1的信號
semaphore=dispatch_semaphore_create(1);
semaphore:等待信號? ?DISPATCH_TIME_FOREVER:等待時(shí)間? wait之后信號量-1,為0
dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);if(imageNames.count>0){imageName=[imageNames lastObject];[imageNames removeObject:imageName];}/**
? ? *? 發(fā)送一個(gè)信號通知,這時(shí)候信號量+1,為1
? ? */dispatch_semaphore_signal(semaphore);
(3) pthread_mutex條件鎖
(4) NSLock
lock,加鎖
unlock,解鎖
tryLock,嘗試加鎖,如果失敗了,并不會阻塞線程,只是立即返回
- (void)getIamgeName:(NSMutableArray *)imageNames{
? ? NSString *imageName;
? ? [lock lock];
? ? if (imageNames.count>0) {
? ? ? ? imageName = [imageNames lastObject];
? ? ? ? [imageNames removeObject:imageName];
? ? }
? ? [lock unlock];
}
(5)條件鎖NSCondition
NSCondition同樣實(shí)現(xiàn)了NSLocking協(xié)議,所以它和NSLock一樣,也有NSLocking協(xié)議的lock和unlock方法,可以當(dāng)做NSLock來使用解決線程同步問題,用法完全一樣。
同時(shí),NSCondition提供更高級的用法。wait和signal,和條件信號量類似。
但是正是因?yàn)檫@種分別加鎖的方式,NSCondition使用wait并使用加鎖后并不能真正的解決資源的競爭。比如我們有個(gè)需求:不能讓m<0。假設(shè)當(dāng)前m=0,線程A要判斷到m>0為假,執(zhí)行等待;線程B執(zhí)行了m=1操作,并喚醒線程A執(zhí)行m-1操作的同時(shí)線程C判斷到m>0,因?yàn)樗麄冊诓煌木€程鎖里面,同樣判斷為真也執(zhí)行了m-1,這個(gè)時(shí)候線程A和線程C都會執(zhí)行m-1,但是m=1,結(jié)果就會造成m=-1.
當(dāng)我用數(shù)組做刪除試驗(yàn)時(shí),做增刪操作并不是每次都會出現(xiàn),大概3-4次后會出現(xiàn)。單純的使用lock、unlock是沒有問題的。
(6) pthread_mutex遞歸鎖
(7) 遞歸鎖NSRecursiveLock 可以重復(fù)加鎖 解鎖 不會造成死鎖
有時(shí)候“加鎖代碼”中存在遞歸調(diào)用,遞歸開始前加鎖,遞歸調(diào)用開始后會重復(fù)執(zhí)行此方法以至于反復(fù)執(zhí)行加鎖代碼最終造成死鎖,這個(gè)時(shí)候可以使用遞歸鎖來解決。使用遞歸鎖可以在一個(gè)線程中反復(fù)獲取鎖而不造成死鎖,這個(gè)過程中會記錄獲取鎖和釋放鎖的次數(shù),只有最后兩者平衡鎖才被最終釋放。
- (void)getIamgeName:(NSMutableArray *)imageNames{
? ? NSString *imageName;
? ? [lock lock];
? ? if (imageNames.count>0) {
? ? ? ? imageName = [imageNames firstObject];
? ? ? ? [imageNames removeObjectAtIndex:0];
? ? ? ? [self getIamgeName:imageNames];
? ? }
? ? [lock unlock];
}
(8) 條件鎖NSConditionLock
也有人說這是個(gè)互斥鎖
NSConditionLock同樣實(shí)現(xiàn)了NSLocking協(xié)議,試驗(yàn)過程中發(fā)現(xiàn)性能很低。
NSConditionLock也可以像NSCondition一樣做多線程之間的任務(wù)等待調(diào)用,而且是線程安全的。
- (void)getIamgeName:(NSMutableArray *)imageNames{
? ? NSString *imageName;
? ? [lock lockWhenCondition:1];? ? //加鎖
? ? if (imageNames.count>0) {
? ? ? ? imageName = [imageNames lastObject];
? ? ? ? [imageNames removeObjectAtIndex:0];
? ? }
? ? [lock unlockWithCondition:0];? ? //解鎖
}
- (void)createImageName:(NSMutableArray *)imageNames{
? ? [lock lockWhenCondition:0];
? ? [imageNames addObject:@"0"];
? ? [lock unlockWithCondition:1];
}
(9) @synchronized代碼塊
- (void)getIamgeName:(int)index{
? ? NSString *imageName;
? ? @synchronized(self) {
? ? ? ? if (imageNames.count>0) {
? ? ? ? ? ? imageName = [imageNames lastObject];
? ? ? ? ? ? [imageNames removeObject:imageName];
? ? ? ? }
? ? }
}
dispatch_queue(DISPATCH_QUEUE_SERIAL)
dispatch_barrier_async柵欄
dispatch_group調(diào)度組
os_unfair_lock互斥鎖
面試題:
1. 線程是什么?進(jìn)程又是什么?兩者之間的區(qū)別?
2. 串行并行 同步異步的區(qū)別?zzzz
并行:充分利用計(jì)算機(jī)的多核,在多個(gè)線程上同步進(jìn)行
并發(fā):在一條線程上通過快速切換,讓人感覺在同步進(jìn)行
3.?子線程與子線程之間是如何進(jìn)行通信的?
4.GCD的一些常用的函數(shù)?(group,barrier,信號量,線程同步)?zzzz
5.如何使用隊(duì)列來避免資源搶奪?
當(dāng)我們使用多線程來訪問同一個(gè)數(shù)據(jù)的時(shí)候,就有可能造成數(shù)據(jù)的不準(zhǔn)確性。這個(gè)時(shí)候我么可以使用線程鎖的來來綁定。也是可以使用串行隊(duì)列來完成。如:fmdb就是使用FMDatabaseQueue,來解決多線程搶奪資源。
6.線程同步的幾個(gè)策略?知道哪幾種鎖及其它們之前區(qū)別?
7.GCD的隊(duì)列(dispatch_queue_t)分哪兩種類型?zzzz
串行串行調(diào)度隊(duì)列
并發(fā)數(shù)量并行調(diào)度隊(duì)列
8.如何用GCD同步若干個(gè)異步調(diào)用?(如根據(jù)若干個(gè)url異步加載多張圖片,然后在都下載完成后合成一張整圖)
使用調(diào)度組追加塊到全局組隊(duì)列,這些塊如果全部執(zhí)行完畢,就會執(zhí)行主調(diào)度隊(duì)列中的結(jié)束處理的塊。
dispatch_queue_t隊(duì)列= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);dispatch_group_t組= dispatch_group_create();dispatch_group_async(group,queue,^ { / *加載圖片1 * / });dispatch_group_async(group,queue,^ { / *加載圖片2 * / });dispatch_group_async(group,queue,^ { / *加載圖片3 * / }); dispatch_group_notify(group,dispatch_get_main_queue(),^ {
? ? ? ? //合并圖片
});
9.dispatch_barrier_async的作用是什么?
可以解決多讀單寫
在并發(fā)等級中,為了保持某些任務(wù)的順序,需要等待一些任務(wù)完成后才能繼續(xù)進(jìn)行,使用barrier來等待任務(wù)完成,避免數(shù)據(jù)dispatch_barrier_async競爭等問題。?函數(shù)會等待追加到并發(fā)調(diào)度隊(duì)列全部執(zhí)行完之后,然后再執(zhí)行dispatch_barrier_async函數(shù)追加的處理,等dispatch_barrier_async追加的處理執(zhí)行結(jié)束之后,并發(fā)調(diào)度隊(duì)列才恢復(fù)之前的動作繼續(xù)執(zhí)行。
打個(gè)比方:某種你們公司周末跟團(tuán)旅游,高速休息站上,司機(jī)說:大家都去上廁所,速戰(zhàn)速決,上完廁所就上高速。超大的公共廁所,大家同時(shí)去,程序猿很快就結(jié)束了,但程序媛就可能會慢一些,即使你第一個(gè)回來,司機(jī)也不會出發(fā),司機(jī)要等待所有人都回來后,才能出發(fā)。dispatch_barrier_async函數(shù)追加的內(nèi)容就如同“上完廁所就上高速”這個(gè)動作。
10.蘋果為什么要廢棄dispatch_get_current_queue?
dispatch_get_current_queue由于派發(fā)模型是按層級來組織的,這意味著排在某條串聯(lián)的中的塊會在其上級串聯(lián)里執(zhí)行。隊(duì)列是否為執(zhí)行同步派發(fā)所用的隊(duì)列這種方法并不總是奏效。dispatch_get_current_queue函數(shù)通常會被用于解決由不可以重入的代碼所引發(fā)的死鎖,然后能用此函數(shù)解決的問題,通常也可以用“特定特定數(shù)據(jù)”來解決。
11.?您是否做過異步的網(wǎng)絡(luò)處理和通訊方面的工作?如果有,能具體介紹一些實(shí)現(xiàn)策略么?
12.有哪些場景是NSOperation比GCD更容易實(shí)現(xiàn)的?(或是NSOperation優(yōu)于GCD的幾點(diǎn),知道多少說多少)
13.?說一說你對線程安全的理解?iOS上怎么保證線程安全?
14.?列舉你知道的線程同步策略?
15.?有哪幾種鎖?各自的原理和優(yōu)缺點(diǎn)?它們之間的區(qū)別是什么?最好可以結(jié)合使用場景來說,瑣是毫秒級別還是微妙級別?
16.?APP內(nèi)通信的方式有什么?
17.?GCD和Operation的比較,有用過其中的一種么?
18.GCD中的Block用到的屬性是否需要__weak修飾
否
19.線程同步有哪些方式?
20.多個(gè)線程同時(shí)訪問一個(gè)資源 需要注意什么?
21.?說一下多線程,你平常是怎么用的?
同時(shí)最多有幾個(gè)線程: 根據(jù)cpu的能力,目測:50個(gè)
22.?NSOperationQueue中的maxConcurrentOperationCount默認(rèn)值
23.?異步一定會開啟新的線程嗎?
24.?GCD如何取消線程?
dispatch_block_cancel類似NSOperation一樣,可以取消還未執(zhí)行的線程。但是沒辦法做到取消一個(gè)正在執(zhí)行的線程。
使用臨時(shí)變量+return 方式取消 正在執(zhí)行的Block
25.?子線程默認(rèn)不會開啟 Runloop,那出現(xiàn) Autorelease 對象如何處理?不手動處理會內(nèi)存泄漏嗎?
在子線程你創(chuàng)建了 Pool 的話,產(chǎn)生的 Autorelease 對象就會交給 pool 去管理。如果你沒有創(chuàng)建 Pool ,但是產(chǎn)生了 Autorelease 對象,就會調(diào)用 autoreleaseNoPage 方法。在這個(gè)方法中,會自動幫你創(chuàng)建一個(gè) hotpage(hotPage 可以理解為當(dāng)前正在使用的 AutoreleasePoolPage,如果你還是不理解,可以先看看 Autoreleasepool 的源代碼,再來看這個(gè)問題 ),并調(diào)用 page->add(obj)將對象添加到 AutoreleasePoolPage 的棧中,也就是說你不進(jìn)行手動的內(nèi)存管理,也不會內(nèi)存泄漏啦!StackOverFlow 的作者也說道,這個(gè)是 OS X 10.9+和 iOS 7+ 才加入的特性
26.聊對于 GCD 的理解,和 GCD 底層是如何進(jìn)行線程調(diào)度的?
首先,線程的生命周期包含:新建(create),就緒(ready),運(yùn)行(run),阻塞(block), 死亡(dead) iOS的線程管理分為,pThread、NSThread、GCD、NSOperationpthread:POSIX標(biāo)準(zhǔn),基于C語言的通用跨平臺線程管理API。NSThread:最早期apple管理線程的apiNSOperation:基于GCD的面向?qū)ο缶€程管理ApiGCD:代替NSThread,C語言的線程管理Api
GCD有一個(gè)線程池底層,并不需要開發(fā)者額外編寫,線程池中的線程有著良好的重用性,當(dāng)一段時(shí)間后這個(gè)線程沒有被調(diào)用胡話,這個(gè)線程就會被銷毀。那么我們只需要向線程隊(duì)列中添加任務(wù),線程隊(duì)列調(diào)度就行了。 如果隊(duì)列中存放的是同步任務(wù),則任務(wù)出隊(duì)后,底層線程池中會提供一條線程供這個(gè)任務(wù)執(zhí)行,任務(wù)執(zhí)行完畢后這條線程再回到線程池。 如果隊(duì)列中存放的是異步的任務(wù),當(dāng)任務(wù)出隊(duì)后,底層線程池會提供一個(gè)線程供任務(wù)執(zhí)行,因?yàn)槭钱惒綀?zhí)行,隊(duì)列中的任務(wù)不需等待當(dāng)前任務(wù)執(zhí)行完畢就可以調(diào)度下一個(gè)任務(wù),這時(shí)底層線程池中會再次提供一個(gè)線程供第二個(gè)任務(wù)執(zhí)行,執(zhí)行完畢后再回到底層線程池中。 這樣就對線程完成一個(gè)復(fù)用,而不需要每一個(gè)任務(wù)執(zhí)行都開啟新的線程,也就從而節(jié)約的系統(tǒng)的開銷,提高了效率。
27.說@synchronized鎖的實(shí)現(xiàn)原理,并說明其中可能存在的問題?
@synchronized是對mutex遞歸鎖的封裝,@synchronized(obj)內(nèi)部會生成obj對應(yīng)的遞歸鎖,然后進(jìn)行加鎖、解鎖操作。最大的問題就是,效率低,傳入對象必須等待之前的鎖執(zhí)行完成之后才能執(zhí)行,無法達(dá)到異步的效果
28.多線程可以訪問同一個(gè)對象嗎,多進(jìn)程呢?
29.有哪些場景是NSOperation比GCD更容易實(shí)現(xiàn)的?(或是NSOperation優(yōu)于GCD的幾點(diǎn),知道多少說多少
GCD底層使用C語言編寫高效、NSOperation是對GCD的面向?qū)ο蟮姆庋b。對于特殊需求,如取消任務(wù)、設(shè)置任務(wù)優(yōu)先級、任務(wù)狀態(tài)監(jiān)聽,NSOperation使用起來更加方便。
NSOperation可以設(shè)置依賴關(guān)系,而GCD只能通過dispatch_barrier_async實(shí)現(xiàn)
NSOperation可以通過KVO觀察當(dāng)前operation執(zhí)行狀態(tài)(執(zhí)行/取消)
NSOperation可以設(shè)置自身優(yōu)先級(queuePriority)。GCD只能設(shè)置隊(duì)列優(yōu)先級(DISPATCH_QUEUE_PRIORITY_DEFAULT),無法在執(zhí)行的block中設(shè)置優(yōu)先級
NSOperation可以自定義operation如NSInvationOperation/NSBlockOperation,而GCD執(zhí)行任務(wù)可以自定義封裝但沒有那么高的代碼復(fù)用度
GCD高效,NSOperation開銷相對高
30. 系統(tǒng)是怎么樣移除一個(gè)isFinished=YES的NSOperation?
通過KVO;
31.??多個(gè)網(wǎng)絡(luò)請求完成后如何執(zhí)行下一步?
(1) 使用GCD的dispatch_group_t
NSString *str = @"http://xxxx.com/";
NSURL *url = [NSURL URLWithString:str];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
dispatch_group_t downloadGroup = dispatch_group_create();
for (int i=0; i<10; i++) {
? ? dispatch_group_enter(downloadGroup);
? ? NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
? ? ? ? NSLog(@"%d---%d",i,i);
? ? ? ? dispatch_group_leave(downloadGroup);
? ? }];
? ? [task resume];
}
dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{
? ? NSLog(@"end");
});
(2)? 使用GCD的信號量dispatch_semaphore_t
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
for (int i=0; i<10; i++) {
? ? NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
? ? ? ? NSLog(@"%d---%d",i,i);
? ? ? ? count++;
? ? ? ? if (count==10) {
? ? ? ? ? ? dispatch_semaphore_signal(sem);
? ? ? ? ? ? count = 0;
? ? ? ? }
? ? }];
? ? [task resume];
}
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_main_queue(), ^{
? ? NSLog(@"end");
});
32.? GCD執(zhí)行原理?
GCD有一個(gè)底層線程池,這個(gè)池中存放的是一個(gè)個(gè)的線程。之所以稱為“池”,很容易理解出這個(gè)“池”中的線程是可以重用的,當(dāng)一段時(shí)間后這個(gè)線程沒有被調(diào)用胡話,這個(gè)線程就會被銷毀。注意:開多少條線程是由底層線程池決定的(線程建議控制再3~5條),池是系統(tǒng)自動來維護(hù),不需要我們程序員來維護(hù)(看到這句話是不是很開心?) 而我們程序員需要關(guān)心的是什么呢?我們只關(guān)心的是向隊(duì)列中添加任務(wù),隊(duì)列調(diào)度即可。
如果隊(duì)列中存放的是同步任務(wù),則任務(wù)出隊(duì)后,底層線程池中會提供一條線程供這個(gè)任務(wù)執(zhí)行,任務(wù)執(zhí)行完畢后這條線程再回到線程池。這樣隊(duì)列中的任務(wù)反復(fù)調(diào)度,因?yàn)槭峭降?,所以?dāng)我們用currentThread打印的時(shí)候,就是同一條線程。
如果隊(duì)列中存放的是異步的任務(wù),(注意異步可以開線程),當(dāng)任務(wù)出隊(duì)后,底層線程池會提供一個(gè)線程供任務(wù)執(zhí)行,因?yàn)槭钱惒綀?zhí)行,隊(duì)列中的任務(wù)不需等待當(dāng)前任務(wù)執(zhí)行完畢就可以調(diào)度下一個(gè)任務(wù),這時(shí)底層線程池中會再次提供一個(gè)線程供第二個(gè)任務(wù)執(zhí)行,執(zhí)行完畢后再回到底層線程池中。
這樣就對線程完成一個(gè)復(fù)用,而不需要每一個(gè)任務(wù)執(zhí)行都開啟新的線程,也就從而節(jié)約的系統(tǒng)的開銷,提高了效率。在iOS7.0的時(shí)候,使用GCD系統(tǒng)通常只能開58條線程,iOS8.0以后,系統(tǒng)可以開啟很多條線程,但是實(shí)在開發(fā)應(yīng)用中,建議開啟線程條數(shù):35條最為合理。
33.?線程死鎖的四個(gè)條件?
? ? 1、互斥:某種資源一次只允許一個(gè)進(jìn)程訪問,即該資源一旦分配給某個(gè)進(jìn)程,其他進(jìn)程就不能再訪問,直到該進(jìn)程訪問結(jié)束。
? ? 2、占有且等待:一個(gè)進(jìn)程本身占有資源(一種或多種),同時(shí)還有資源未得到滿足,正在等待其他進(jìn)程釋放該資源。
? ? 3、不可搶占:別人已經(jīng)占有了某項(xiàng)資源,你不能因?yàn)樽约阂残枰撡Y源,就去把別人的資源搶過來。
? ? 4、循環(huán)等待:存在一個(gè)進(jìn)程鏈,使得每個(gè)進(jìn)程都占有下一個(gè)進(jìn)程所需的至少一種資源。
34.?