多線程概念
- 程序: 由源代碼生成的可執行應用.
- 進程: 一個正在運行的程序可以看做一個進程. (例如: 正在運行的QQ就是一個進程) , 進程擁有獨立運行所需的全部資源.
- 線程: 程序中獨立運行的代碼段.
注:一個進程是由一或多個線程組成. 進程只負責資源的調度和分配,線程才是程序真正的執行單元,負責代碼的執行.
- 單線程:
1、每個正在運行的程序(即 進程),至少包含一個線程, 這個線程叫 主線程
2、主線程在程序啟動時被創建,用于執行mian函數
3、只有一個主線程的程序,稱作單線程程序
3、在單線程程序中,主線程負責執行程序的所有代碼(UI 展現以及刷新,網絡請求, 本地存儲等). 這些代碼只能順序執行, 無法并發執行 .
- 多線程:
1、擁有多個線程的程序,稱作多線程程序
2、iOS 允許用戶自己開辟新的線程, 相對于主線程來講, 這些線程, 稱作子線程
3、可以根據需要開辟若干子線程
4、子線程和主線程 都是 獨立 的運行單元, 各自的執行互不影響, 因此能夠并發執行
- 單線程,多線程區別 :
1、單線程程序: 只有一個線程, 即主線程, 代碼順序執行,容易出現代碼阻塞(頁面假死)
2、多線程程序: 有多個線程, 線程間獨立運行, 能有效的避免代碼阻塞,并且提高程序的運行性能.
注意: iOS關于UI的添加和刷新必須在主線程中操作.//開發中依賴于多線程: 網絡 :(主線程:(UI),子線程(取數據))
- iOS多線程實現種類
NSObject
NSThread
NSOperationQueue
GCD
NSObject 和 NSThread
#pragma mark ---------NSObject 開辟子線程
// NSObject 開辟子線程
// 參數 1: selector, 子線程執行的代碼(方法名)
// 參數 2: 表示selector傳遞的參數
[self performSelectorInBackground:@selector(sayHi) withObject:@"hahah"];
#pragma mark ---------NSThread 手動開辟子線程
// NSThread 開辟一個子線程
// 參數 1: target
// 參數 2: action
// 參數 3: 傳參
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(sayHi) object:nil];
//開啟子線程 [thread start];
//取消 (給線程發送結束消息,不會真正的取消掉線程,而是標記 這個被取消了)
[thread cancel];
//立即結束線程
[NSThread exit];
//對于NSObject 和 NSThread實現的多線程,在任務完成之后,線程會被自動釋放
//判斷一個線程是否正在執行
[thread isExecuting];
//判斷一個線程是否完成了任務(是否執行完畢)
[thread isFinished];
#pragma mark -----------NSThread 自動開辟一個線程
//使用NSThread自動開辟一個線程
//不需要手動開啟線程
[NSThread detachNewThreadSelector:@selector(sayHi) toTarget:self withObject:nil];
//幾秒后執行某件事情
[self performSelector:@selector(time) withObject:self afterDelay:3.0f];
}
//讓線程休眠2秒
[NSThread sleepForTimeInterval:2];
- (void)sayHi{
// [NSThread currentThread]; 獲取當前的線程
NSLog(@" %@ ", [NSThread currentThread]);
// [NSThread mainThread]; 獲取主線程
NSLog(@" %@ ", [NSThread mainThread]);
// [NSThread isMainThread] 判斷當前線程是不是主線程
NSLog(@" %d ", [NSThread isMainThread]);
#pragma mark ----------NSObject
//NSObject中回到主線程去做某事
// 參數 1: 回到主線程做的事情
// 參數 2: 傳遞的參數
// 參數 3: NO:當前的線程任務已經結束才去做 YES: 執行完 selector任務后才執行當前線程的其他任務
[self performSelectorOnMainThread:@selector(onMainThread) withObject:nil waitUntilDone:YES];
\ #pragma mark -----------NSThread 手動
for (int i = 0; i < 10000; i++) { NSLog(@" %d ", i);
if (i == 5000) {
//關閉線程
// 寫在哪里 哪個線程就關閉了, 注意 不要隨意的使用. 使用的時候一定要注意當前的線程 是否主線程
[NSThread exit];
}
}
}
- (void)onMainThread{
self.view.backgroundColor = [UIColor orangeColor];
NSLog(@"mian --> %@ ", [NSThread mainThread]);
NSLog(@"current --> %@", [NSThread currentThread]);
}```
-----
NSOperation和NSOperationQueue
------
------
- NSOperation :
>1、NSOperation類, 在MVC中屬于M, 是用來封裝單個任務相關的代碼和數據的抽象類
2、因為它抽象的,不能夠直接使用這個類,而是使用子類(NSInvocationOperation或NSBlockOperation) 來執行實際任務.
3、NSOperation(含子類), 只是一個操作, 本身無主線, 子線程之分, 可在任意線程中使用. 通常與NSOperationQueue結合使用.
- NSOperationQueue
>1、NSOperationQueue是操作隊列,它用來管理一組Operation對象的執行,會根據需要自動為Operation開辟合適數量的線程,以完成任務并行執行
2、其中NSOperation可以調節它在隊列中的優先級 (使用addDependency: 設置依賴關系)
3、當最大并發數設置為1的時候,能實現線程同步 (串行執行);
-------
//NSOperation 是一個抽象類,不能直接使用
//NSOperation類及其子類本身不會進行線程的創建
\#pragma mark —————NSInvocationOperation
//通過NSInvocationOperation類來創建一個NSOperation對象
```code
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(hehehe) object:nil];`
//operation 在單獨使用的時候 一定要調用開始方法
` [operation start];```
\#pragma mark —————NSBlockOperation
```code
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"block main --> %@", [NSThread mainThread]);
NSLog(@"block current --> %@", [NSThread currentThread]); }];
//operation 在單獨使用的時候 一定要調用開始方法
[blockOperation start];```
\#pragma mark —————NSOperationQueue
` NSOperationQueue *queue = [[NSOperationQueue alloc]init];`
//隊列添加operation子類,并調用方法
` [queue addOperation:operation];
[queue addOperation:blockOperation]; `
//依賴關系 (只有 參數線程 執行完,才能執行,之前是隨機交錯進行的)
` [operation addDependency:blockOperation];`
//獲取 系統提供的隊列
```code
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];}
- (void)hehehe{
NSLog(@"hehehe main --> %@", [NSThread mainThread]);
NSLog(@"hehehe current --> %@", [NSThread currentThread]);
NSLog(@" %d ", [NSThread isMainThread]);
}```
```code
- (void)touchesBegan:(NSSet<UITouch *> \*)touches withEvent:(UIEvent \*)event{
//NSOperationQueue 是一個隊列管理器,可以根據operation任務自己,分配線程,自己管理線程的生命周期
//在開發過程中,我們不需要管理線程的創建和銷毀
//NSOperationQueue 創建的是n個并行的線程
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//最大線程并發數
//設置這個參數之后,NSOperationQueue表示同時執行任務的最大數量
//即使只執行一個任務,系統有時候也會開辟多個線程去執行這個任務
queue.maxConcurrentOperationCount = 1;
for (int i = 0; i < 10; i++) ``
{
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"current --> %@ main --> %@",[NSThread currentThread],[NSThread mainThread]); }];
[queue addOperation:blockOperation]; }}```
------
GCD
-------
--------
##Grand Central Dispatch (GCD)是Apple開發的一種多核編程技術.主要用于優化應用程序以支持多核處理器以及其他對稱處理系統.
###GCD提供函數實現多線程開發,性能更好,功能也更加強大.
- ###核心概念:
1、任務: 具有一個訂功能的代碼段.一般是一個block或者函數
2、分發隊列: GCD以隊列的方式進行工作,FIFO(先入先出隊列)
3、GCD會根據分發隊列的類型, 創建合適數量的線程執行隊列中的任務
- ###GCD中的兩種隊列 dispatch_queue:
- SerialQueue(串行) : 一次只執行一個任務. Serial queue通常用于同步訪問,特定的資源或數據.當你創建多個Serial queue時,雖然他們各自是同步執行的,但Serial queue于Serial queue之間是并發執行的.SerialQueue能實現線程同步.
- Concurrent(并發) : 可以并發地執行多個任務,但是遵守FIFO(先入先出隊列 )
--------
--------
\#pragma mark ----------GCD串行隊列
// 1) 系統提供的一個串行隊列
// 使用系統提供的串行隊列 (主隊列,也就是在主線程里一次執行任務)
` dispatch_queue_t queue = dispatch_get_main_queue();`
// 2) 創建一個串行隊列
// 參數 1: 自己創建隊列的名(蘋果推薦使用反向域名去命名注意沒有@)
// 參數 2: 系統提供好的一個宏(DISPATCH_QUEUE_SERIAL=NULL)
// 這種方式創建的隊列,會開辟子線程去執行任務
` dispatch_queue_t queue = dispatch_queue_create("name", DISPATCH_QUEUE_SERIAL);`
------
------
\#pragma mark ----------GCD并行隊列 // 1) 使用系統提供的并行隊列
// 參數 1: 表示隊列的優先級 (
DISPATCH_QUEUE_PRIORITY_BACKGROUND,
DISPATCH_QUEUE_PRIORITY_LOW,
DISPATCH_QUEUE_PRIORITY_HEIGH,
DISPATCH_QUEUE_PRIORITY_DEFAULT)
// 參數 2: 系統保留字段
`dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);`
// 2) 創建并行隊列
` dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);`
--------
--------
\#pragma mark ----------GCD 功能
//使用dispatch_async() 向隊列添加任務,任務會排隊執行
// 任務雖會順序排隊執行,但如果用并發隊列(CONCURRENT),可能輸出順序不一樣.
```code
dispatch_async(queue, ^{
NSLog(@"1 mian --> %@, current --> %@",[NSThread mainThread],[NSThread currentThread]); });
dispatch_async(queue, ^{
NSLog(@"2 mian --> %@, current --> %@",[NSThread mainThread],[NSThread currentThread]); });
dispatch_async(queue, ^{
NSLog(@"3 mian --> %@, current --> %@",[NSThread mainThread],[NSThread currentThread]);
}); ```
// dispatch_after()
//往隊列中添加任務,任務不但會排隊,還會在延遲的時間點執行
```code
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"已經3秒之后了");
});```
//dispatch_apply();
//往隊列中添加任務,任務會重復執行n次
// 參數 1: 一共執行次數
// 參數 2: 執行的隊列
// 參數 3: 當前索引
```code
dispatch_apply(3, queue, ^(size_t index) {
NSLog(@" %zu ", index);
});```
//分組
//創建一個分組
`dispatch_group_t group = dispatch_group_create(); `
//創建一個隊列
`dispatch_queue_t queue = dispatch_queue_create("000", DISPATCH_QUEUE_CONCURRENT); `
//向分組中添加一個任務
` dispatch_group_async(group, queue, ^{
NSLog(@"1"); }); `
//向分組添加 最后執行的任務(不能添加為第一個)
`dispatch_group_notify(group, queue, ^{
NSLog(@"last one");
})`
//將任務添加到隊列,此任務執行的時候,其他任務停止執行,所以它輸出順序不改變
```code
dispatch_barrier_async(queue, ^{
NSLog(@"不變位置的2"); });
dispatch_group_async(group, queue, ^{
NSLog(@"3");
});
}```
####**dispatch_once() //將任務添加到隊列, 但任務在程序運行過程中,只執行一次**
//創建單例類
\#import "MyObject.h"static MyObject *object = nil;
\+ (MyObject *)sharedMyObject{
//表示同意時間內, 只有一個線程可以訪問block塊里面的內容
//dispatch_once 系統封裝好的代碼塊
```code
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (object == nil) {
object = [MyObject new]; } });
return object;}```
>dispatch_sync() //將任務添加到隊列, block不執行完,下面代碼不會執行
dispatch_async() //將任務添加到隊列,不等內部block執行完,就去執行下面代碼
dispatch_async_f() //將任務添加到隊列, 任務是函數,非Block
--------
線程間的通信
---------
---------
>線程間通信分為兩種:
- 主線程進入子線程 (前面的方法都可以)
- 子線程回到主線程
\#pragma mark ----------NSObject
\ -(void)viewDidLoad{
//NSObject中回到主線程去做某
// 參數 1: 回到主線程做的事情
// 參數 2: 傳遞的參數
// 參數 3: 知道當前的線程已經結束才去做
```code
[self performSelectorOnMainThread:@selector(onMainThread) withObject:nil waitUntilDone:YES];
}`
\- (void)onMainThread{
`self.view.backgroundColor = [UIColor orangeColor];
NSLog(@"mian --> %@ ", [NSThread mainThread]);
NSLog(@"current --> %@", [NSThread currentThread]);`
}
\#pragma mark —————GCD
\- (void)loadData{
`NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.baidu.com"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error == nil) {
dispatch_async(dispatch_get_main_queue(), ^{
//這里去做更新UI的事情
}); } }];
}```
--------
- ###線程互斥
> 1、多線程并行編程中,線程間同步與互斥是一個很有技巧也是很容易出錯的地方.
2、線程間互斥應對的是這種場景: 多個線程操作統一資源(即某個對象),需要保證線程在對資源的狀態(即對象的成員變量)進行一些非原子性操作后,狀態仍然正確.