iOS基礎--多線程簡單總結(NSThread、NSOperation、NSOperationQueue))

原來一切都來不及
多線程.png

多線程概念


  • 程序: 由源代碼生成的可執行應用.
  • 進程: 一個正在運行的程序可以看做一個進程. (例如: 正在運行的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、線程間互斥應對的是這種場景: 多個線程操作統一資源(即某個對象),需要保證線程在對資源的狀態(即對象的成員變量)進行一些非原子性操作后,狀態仍然正確.
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,321評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,559評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,442評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,835評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,581評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,922評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,931評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,096評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,639評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,374評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,591評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,104評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,789評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,196評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,524評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,322評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,554評論 2 379

推薦閱讀更多精彩內容

  • NSThread 第一種:通過NSThread的對象方法 NSThread *thread = [[NSThrea...
    攻城獅GG閱讀 830評論 0 3
  • 背景 擔心了兩周的我終于輪到去醫院做胃鏡檢查了!去的時候我都想好了最壞的可能(胃癌),之前在網上查的癥狀都很相似。...
    Dely閱讀 9,262評論 21 42
  • 一、多線程簡介: 所謂多線程是指一個 進程 -- process(可以理解為系統中正在運行的一個應用程序)中可以開...
    尋形覓影閱讀 1,064評論 0 6
  • 沒有去西安之前,西安在我的印象中就像《三槍拍案驚奇》里的大漠北的感覺,總是有一種這地方兒缺水的感覺先入為主,以至于...
    苗子愛吃肉閱讀 454評論 2 1
  • 已是22點了,睡前照例不經意查閱下微信,竟發現我所在寫作組的朋友們都在陸續地上傳作業,十一月份雖說已沒規定要交作業...
    與你畫夕陽閱讀 394評論 0 0