

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