多線程

線程和進(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.?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容