線程是進程的基本執行單元,一個進程的全部任務都在線程中執行 進程要想執行任務,必須得有線程,進程至少要有一條線程。 程序啓動會默認開啓一條線程,這條線程被稱爲主線程或UI
線程。服務器
進程是指在系統中正在運行的一個應用程序。每一個進程之間是獨立的,每一個進程均運行在其專用的且受保護的內存。多線程
I/O
、cpu
等,可是進程之間的資源是獨立的。例如,有一個進程是打掃教室,其中掃地、擦桌子、擦玻璃、擦黑板就是不一樣的線程。併發
CPU
在單位時間片裏在各個線程之間切換。函數
線程的生命週期包含5個階段,包括:新建、準備、運行、阻塞、銷燬。oop
start()
方法後,此時線程處於等待CPU
分配資源階段,誰先搶的CPU
資源,誰開始執行。CPU
資源時,便進入運行狀態,run
方法定義了線程的操做和功能。sleep()
、wait()
以後線程就處於了阻塞狀態,這個時候須要其餘機制將處於阻塞狀態的線程喚醒,好比調用notify
或者notifyAll()
方法。喚醒的線程不會馬上執行run
方法,它們要再次等待CPU
分配資源進入運行狀態。 銷燬:若是線程正常執行完畢後或線程被提早強制性的終止或出現異常致使結束,那麼線程就要被銷燬,釋放資源。整個流程以下所示:post
多線程處理是經過線程池來進行的,處理過程當中將任務添加到隊列,而後在建立線程後自動啓動這些任務。線程池中的線程都是後臺線程。每一個線程都有默認的堆棧大小,以默認的優先級運行,並處在多線程單元中。線程池的性能
飽和策略分爲四種,均實現的RejectedExecutionHandler
接口:atom
AbortPolicy
直接拋出RejectedExecutionExeception
異常來阻止系統正常運行CallerRunsPolicy
將任務回退到調用者DisOldestPolicy
丟掉等待最久的任務DisCardPolicy
直接丟棄任務優勢:spa
CPU
,內存)缺點:
KB
)CPU
在調用線程上的開銷就越大iOS
多線程技術iOS
多線程有不少技術,下面作了一下對比:
對比得知,咱們通常主要使用的仍是GCD
和NSOperation
。
runloop
的關係runloop
與線程是一一對應的,一個runloop
對應一個核心的線程。爲何說是核心的,是由於runloop
是能夠嵌套的,可是核心的只能有一個,他們的關係保存在一個全局的字典裏。runloop
是來管理線程的,當線程的runloop
被開啓後,線程會在執行完任務後進入休眠狀態,有了任務就會被喚醒去執行任務。runloop
在第一次獲取時被建立,在線程結束時被銷燬。runloop
在程序一啓動就默認建立好了。runloop
是懶加載的,只有當咱們使用的時候纔會建立,因此在子線程用定時器要注意,確保子線程的runloop
被建立,否則定時器不會回調。多線程之間的通信,除過簡單的獲取主線程,還有2種方式。
performSelector:onThread
。好比說有A
、B
兩個線程,咱們能夠在A
線程裏使用該方法,調用B
的方法,而後再在B
線程裏使用該方法調用A
線程的方法,這樣就實現了線程之間的通信。- (void)performSelectorOnMainThread:(SEL)aSelector
withObject:(nullable id)arg
waitUntilDone:(BOOL)wait
- (void)performSelector:(SEL)aSelector
onThread:(NSThread *)thr
withObject:(nullable id)arg waitUntilDone:(BOOL)wait
複製代碼
NSPort
。NSPort
使用的方式爲接收線程中註冊NSPort
,在另外的線程中使用此port
發送消息,則被註冊線程會收到相應消息,而後最終在主線程裏調用某個回調函數。NSPort
有3個子類,NSSocketPort
、NSMessagePort
、NSMachPort
,但在iOS
下只有NSMachPort
可用。
NSMachPort
的方式以下:
A
線程的port
,將port
加入runloop
@property (nonatomic, strong) NSPort *aPort;
@property (nonatomic, strong) TPerson *person;
//1. 建立主線程的port
// 子線程經過此端口發送消息給主線程
self.aPort = [NSMachPort port];
//2. 設置port的代理回調對象
self.aPort.delegate = self;
//3. 把port加入runloop,接收port消息
[[NSRunLoop currentRunLoop] addPort:self.aPort forMode:NSDefaultRunLoopMode];
self.person = [[TPerson alloc] init];
[NSThread detachNewThreadSelector:@selector(personLaunchThreadWithPort:) toTarget:self.person withObject:self.aPort];
複製代碼
person
類中建立一個響應線程的方法@interface TPerson : NSObject
- (void)personLaunchThreadWithPort:(NSPort *)port;
@end
#import "TPerson.h"
@interface TPerson()
@property (nonatomic, strong) NSPort *bPort;
@end
@implementation TPerson
- (void)personLaunchThreadWithPort:(NSPort *)port{
@autoreleasepool {
// 設置子線程名字
[[NSThread currentThread] setName:@"TPersonThread"];
// 開啓runloop
[[NSRunLoop currentRunLoop] run];
// 建立本身port
self.bPort = [NSMachPort port];
// 完成向主線程port發送消息
NSData *data = [@"portPass" dataUsingEncoding:NSUTF8StringEncoding];
NSMutableArray *array =[[NSMutableArray alloc]initWithArray:@[data,self.bPort]];
// 發送消息到A線程
// 第一個參數:發送時間。
// msgid 消息標識。
// components,發送消息附帶參數。
// reserved:爲頭部預留的字節數
[port sendBeforeDate:[NSDate date]
msgid:10086
components:array
from:self.bPort
reserved:0];
}
}
複製代碼
A
線程實現的代理方法,獲取回調的信息#pragma mark - NSMachPortDelegate
- (void)handlePortMessage:(NSPortMessage *)message{
NSLog(@"VC == %@",[NSThread currentThread]);
NSLog(@"從person 傳過來一些信息:");
// NSLog(@"localPort == %@",[message valueForKey:@"localPort"]);
// NSLog(@"remotePort == %@",[message valueForKey:@"remotePort"]);
// NSLog(@"receivePort == %@",[message valueForKey:@"receivePort"]);
// NSLog(@"sendPort == %@",[message valueForKey:@"sendPort"]);
// NSLog(@"msgid == %@",[message valueForKey:@"msgid"]);
// NSLog(@"components == %@",[message valueForKey:@"components"]);
NSArray *messageArr = [message valueForKey:@"components"];
NSString *dataStr = [[NSString alloc] initWithData:messageArr.firstObject encoding:NSUTF8StringEncoding];
NSLog(@"傳過來一些信息 :%@",dataStr);
NSPort *destinPort = [message valueForKey:@"remotePort"];
if(!destinPort || ![destinPort isKindOfClass:[NSPort class]]){
NSLog(@"傳過來的數據有誤");
return;
}
}
複製代碼
atomic
與nonatomic
nonatomic
非原子性,非線程安全,可是性能高一些,適合內存小的移動設備atomic
原子屬性(線程安全),針對多線程設計的,是默認的。保證同一時間只有一個線程可以寫入(可是同一個時間多個線程均可以取值),在屬性的 setter
方法中添加了一把鎖(自旋鎖)。屬於單寫多讀:單個線程寫入,多個線程能夠讀取。可是須要消耗大量的資源。通常iOS
開發中,關於線程相關須要注意的是:
nonatomic