一籍凝、多線程概述
程序周瞎、進(jìn)程、線程
程序:由源代碼生成的可執(zhí)行應(yīng)用(例如:QQ.app)
進(jìn)程:一個正在運行的程序可以看做一個進(jìn)程静浴。(例如:正在運行的 QQ就是一個進(jìn)程)堰氓,進(jìn)程擁有獨立運行所需的全部資源
線程:程序中獨立運行的代碼段。(例如:接收QQ消息的代碼)
一個進(jìn)程是由一或多個線程組成苹享。進(jìn)程只負(fù)責(zé)資源的調(diào)度和分配双絮,線程才是程序真正的執(zhí)行單元,負(fù)責(zé)代碼的執(zhí)行得问。
單線程
- 每個正在運行的程序(即進(jìn)程)囤攀,至少包含一個線程,這個線程叫主線程宫纬。
- 主線程在程序啟動時被創(chuàng)建焚挠,用于執(zhí)行main函數(shù)。
- 只有一個線程即主線程的程序漓骚,稱作單線程程序蝌衔。
- 在單線程程序中,主線程負(fù)責(zé)執(zhí)行程序的所有代碼(UI展現(xiàn)以及刷新蝌蹂,網(wǎng)絡(luò)請求噩斟,本地存儲等等)。這些代碼只能順序執(zhí)行孤个,無法并發(fā)執(zhí)行剃允。
多線程
- 擁有多個線程的程序,稱作多線程程序。
- iOS允許用戶自己開辟新的線程斥废,相對于主線程來講椒楣,這些線程,稱作子線程牡肉。
- 可以根據(jù)需要開辟若干子線程
- 子線程和主線程都是獨立的運行單元捧灰,各自的執(zhí)行互不影響,因此能夠并發(fā)執(zhí)行荚板。
單凤壁、多線程區(qū)別
- 單線程程序:只有一個線程,即主線程跪另,代碼順序執(zhí)行,容易出現(xiàn)代碼阻塞(頁面假死)煤搜。
- 多線程程序:有多個線程免绿,線程間獨立運行,能有效地避免代碼阻塞擦盾,并且提高程序的運行性能嘲驾。
- 注意:iOS中關(guān)于UI的添加和刷新必須在主線程中操作。
二迹卢、iOS平臺下的多線程
iOS多線程實現(xiàn)種類
- NSThread
- NSOperationQueue
- NSObject
- GCD
---------------------NSThread和NSObject------------------
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"測試");
/*
// 線程休眠5秒
[NSThread sleepForTimeInterval:5.0];
// [NSThread currentThread] 獲取當(dāng)前的線程
NSLog(@"current ==== %@", [NSThread currentThread]);
// [NSThread mainThread] 獲取主線程
NSLog(@"main ==== %@", [NSThread mainThread]);
// [NSThread isMainThread] 判斷當(dāng)前線程是否是主線程
NSLog(@"isMainThread = %d", [NSThread isMainThread]);
*/
// NSThread手動開辟子線程
// 第一個參數(shù):target
// 第二個參數(shù):action
// 第三個參數(shù):傳參
// NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(sayHi) object:nil];
// 讓線程開始執(zhí)行
// [thread start];
// [thread isExecuting] 判斷一個線程是否正在執(zhí)行
// NSLog(@"isExecuting = %d", [thread isExecuting]);
// [thread isFinished] 判斷一個線程是否完成了任務(wù)(是否執(zhí)行完畢)
// NSLog(@"isFinished = %d", [thread isFinished]);
// 結(jié)束線程 給線程發(fā)送結(jié)束消息
// [thread cancel];
// NSThread自動開辟線程
// [NSThread detachNewThreadSelector:@selector(sayHi) toTarget:self withObject:nil];
// NSObject開辟子線程
// 第一個參數(shù):selector辽故,子線程執(zhí)行的代碼
// 第二個參數(shù):表示selector傳遞的參數(shù)
// 這是NSObject中存在的后臺執(zhí)行的方法
[self performSelectorInBackground:@selector(sayHi) withObject:nil];
self.view.backgroundColor = [UIColor cyanColor];
}
- (void)sayHi
{
NSLog(@"current ==== %@", [NSThread currentThread]);
NSLog(@"main ==== %@", [NSThread mainThread]);
for (int i = 0; i < 1000; i++)
{
NSLog(@"i ==== %d",i);
if (i == 500)
{
// 立即結(jié)束線程
// 寫在哪里 哪個線程就關(guān)閉了 注意:不要隨意地使用,使用時一定要注意當(dāng)前的線程是否是主線程
// [NSThread exit];
}
}
// NSObject中存在的回到主線程執(zhí)行的方法
// 第一個參數(shù):回到主線程做的事情
// 第二個參數(shù):傳遞的參數(shù)
// 第三個參數(shù):直到當(dāng)前的線程已經(jīng)結(jié)束才去執(zhí)行這個方法
[self performSelectorOnMainThread:@selector(onMainThread) withObject:nil waitUntilDone:YES];
}
- (void)onMainThread
{
self.view.backgroundColor = [UIColor orangeColor];
NSLog(@"current ==== %@", [NSThread currentThread]);
NSLog(@"main ==== %@", [NSThread mainThread]);
NSLog(@"isMainThread ==== %d", [NSThread isMainThread]);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
---------------NSOperation和NSOperationQueue-------------
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
/*
NSOperation類腐碱,在MVC中屬于M誊垢,是用來封裝單個任務(wù)相關(guān)的代碼和數(shù)據(jù)的抽象類
因為它是抽象的,不能夠直接使用這個類症见,而是使用子類(NSInvocationOperation或NSBlockOperation)來執(zhí)行實際任務(wù)
NSOperation(含子類)喂走,只是一個操作,本身無主線程谋作、子線程之分芋肠,可在任意線程中使用,它們本身和多線程沒有任何關(guān)系遵蚜,不會進(jìn)行線程的創(chuàng)建帖池,只是封裝了一定的代碼段和數(shù)據(jù)去實現(xiàn)一個功能。通常與NSOperationQueue結(jié)合使用
*/
// NSInvocationOperation類封裝了執(zhí)行操作的target和要執(zhí)行的action
// NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(hehehe) object:nil];
// 開始去執(zhí)行
// [operation start];
// NSBlockOperation封裝了需要執(zhí)行的代碼塊
// NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
//
// NSLog(@"main1 ==== %@", [NSThread mainThread]);
// NSLog(@"current1 ==== %@",[NSThread currentThread]);
//
// }];
// 開始去執(zhí)行
// [blockOperation start];
/*
NSOperationQueue是操作隊列吭净,它用來管理一組NSOperation對象的執(zhí)行睡汹,會根據(jù)需要自動為NSOperation開辟合適數(shù)量的線程,以完成任務(wù)的并行執(zhí)行
在開發(fā)過程中攒钳,我們不需要管理線程的創(chuàng)建和銷毀
NSOperation類可以調(diào)節(jié)它在隊列中的優(yōu)先級(使用addDependency:設(shè)置依賴關(guān)系)
當(dāng)最大并發(fā)數(shù)設(shè)置為1的時候能實現(xiàn)線程同步(串行執(zhí)行)
*/
// NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// queue.maxConcurrentOperationCount = 2;
// 給兩個NSOperation對象添加一個依賴關(guān)系帮孔,它們就會根據(jù)依賴關(guān)系順序執(zhí)行
// [operation addDependency:blockOperation];
// [queue addOperation:blockOperation];
// [queue addOperation:operation];
// 獲取系統(tǒng)提供的主隊列
// NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
}
- (void)hehehe
{
NSLog(@"main ==== %@", [NSThread mainThread]);
NSLog(@"current ==== %@",[NSThread currentThread]);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 10;
for (int i = 0; i < 10; i++)
{
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%dcurrent = %@, main = %@", i, [NSThread currentThread], [NSThread mainThread]);
}];
[queue addOperation:operation];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
---------------GCD(Grand Central Dispatch)-----------------
GCD簡介
Grand Central Dispatch(GCD)是Apple開發(fā)的一種多核編程技術(shù)。主要用于優(yōu)化應(yīng)用程序以支持多核處理器以及其他對稱多處理系統(tǒng)
GCD提供函數(shù)實現(xiàn)多線程開發(fā),性能更高功能也更加強大
它首次發(fā)布在Mac OS X10.6文兢,iOS4及以上也可用
GCD核心概念
任務(wù):具有一定功能的代碼段晤斩。一般是一個block或者函數(shù)
分發(fā)隊列:GCD以隊列的方式進(jìn)行工作,F(xiàn)IFO
GCD會根據(jù)分發(fā)隊列的類型姆坚,創(chuàng)建合適數(shù)量的線程執(zhí)行隊列中的任務(wù)
GCD中兩種隊列
dispatch queue分為下面2種:
SerialQueue:一次只執(zhí)行一個任務(wù)澳泵。Serial queue通常用于同步訪問特定的資源和數(shù)據(jù)。當(dāng)你創(chuàng)建多個Serial queue時兼呵,雖然它們各自是同步執(zhí)行的兔辅,但Serial queue與Serial queue之間是并發(fā)執(zhí)行的。SerialQueue能實現(xiàn)線程同步
Concurrent:可以并發(fā)地執(zhí)行多個任務(wù)击喂,但是遵守FIFO
--------------------ViewController.m----------------------
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
#pragma mark ---- GCD串行隊列
// 系統(tǒng)提供的一個串行隊列
// 里面的任務(wù)是在主線程依次去執(zhí)行维苔,也就是串行
// 這個方法比較常用
// dispatch_queue_t queue = dispatch_get_main_queue();
// 創(chuàng)建一個串行隊列
// 第一個參數(shù):隊列的名字(蘋果推薦使用反向域名去命名),也可以是0
// 第二個參數(shù):系統(tǒng)提供好的一個宏懂昂,表示串行隊列系統(tǒng)保留字段
// 這種方式創(chuàng)建的隊列介时,它會自己去開辟一個子線程去完成隊列里面的任務(wù)
// dispatch_queue_t queue = dispatch_queue_create(0 , DISPATCH_QUEUE_SERIAL);
#pragma mark ---- 并行隊列
// 使用系統(tǒng)提供的并行隊列
// 這個方法比較常用
// global queue 是蘋果里面的全局隊列,有4個優(yōu)先級
// DISPATCH_QUEUE_PRIORITY_BACKGROUND 表示隊列的優(yōu)先級
// 第二個參數(shù)是蘋果預(yù)留的參數(shù)凌彬,為了以后去使用沸柔,目前沒有用到,填寫0
// dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
// 自己創(chuàng)建一個并行隊列
// 第一個參數(shù):表示這個隊列的名字
// 第二個參數(shù):系統(tǒng)提供好的一個宏铲敛,表示并行隊列
// dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
// 使用dispatch_async向隊列中添加任務(wù)
// dispatch_async(queue, ^{
//
// NSLog(@"main ==== %@褐澎,current ==== %@", [NSThread mainThread], [NSThread currentThread]);
//
// });
//
// dispatch_async(queue, ^{
//
// NSLog(@"main1 ==== %@,current1 ==== %@", [NSThread mainThread], [NSThread currentThread]);
//
// });
//
// dispatch_async(queue, ^{
//
// NSLog(@"main2 ==== %@伐蒋,current2 ==== %@", [NSThread mainThread], [NSThread currentThread]);
//
// });
//
// dispatch_async(queue, ^{
//
// NSLog(@"main3 ==== %@工三,current3 ==== %@", [NSThread mainThread], [NSThread currentThread]);
//
// });
//
// // 延遲執(zhí)行一段代碼 幾秒之后做某件事
// dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//
// NSLog(@"已經(jīng)5秒之后了");
//
// });
#pragma mark ---- 重復(fù)的向一個隊列中添加一系列的任務(wù)
// dispatch_queue_t queue = dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);
// 使用dispatch_apply()往隊列中添加任務(wù),任務(wù)會重復(fù)執(zhí)行n次
// 第一個參數(shù):執(zhí)行的次數(shù)
// 第二個參數(shù):在哪個隊列里面去執(zhí)行
// 第三個參數(shù):當(dāng)前索引
// dispatch_apply(100, queue, ^(size_t index) {
//
// NSLog(@"index ==== %zu", index);
//
// });
#pragma mark ---- 分組
// 創(chuàng)建一個分組
// dispatch_group_t主要用于把一些不相關(guān)的任務(wù)歸為一組咽弦,組里面放的是隊列
// dispatch_group_t group = dispatch_group_create();
// 創(chuàng)建一個并行隊列
// dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
// 將任務(wù)添加到隊列中徒蟆,此任務(wù)執(zhí)行的時候,其他任務(wù)停止執(zhí)行
// dispatch_barrier_async(queue, ^{
//
// NSLog(@"我執(zhí)行時型型,其他任務(wù)停止執(zhí)行");
//
// });
// dispatch_group_async作用是往組里面的隊列添加任務(wù)
// 將任務(wù)添加到隊列中段审,并添加分組標(biāo)記
// dispatch_group_async(group, queue, ^{
//
// NSLog(@"我是第1個任務(wù)");
//
// });
// dispatch_group_notify作用:監(jiān)聽組里面的任務(wù),等到組里面的任務(wù)全部執(zhí)行完成之后闹蒜,才會執(zhí)行它里面的任務(wù)
// 注意:在執(zhí)行dispatch_group_notify之前最起碼要向隊列中放置一個任務(wù)才可以寺枉,否則notify里面的任務(wù)不會等待小組里面的其他任務(wù)執(zhí)行完才執(zhí)行
// dispatch_group_notify(group, queue, ^{
//
// NSLog(@"無論如何,我是最后一個");
//
// });
//
// dispatch_group_async(group, queue, ^{
//
// NSLog(@"我是第2個任務(wù)");
//
// });
//
// dispatch_group_async(group, queue, ^{
//
// NSLog(@"我是第3個任務(wù)");
//
// });
//
// dispatch_group_async(group, queue, ^{
//
// NSLog(@"我是第4個任務(wù)");
//
// });
// dispatch_once() 將任務(wù)添加到隊列中绷落,但任務(wù)在程序運行過程中姥闪,只執(zhí)行一次
// dispatch_sync() 將任務(wù)添加到隊列中block不執(zhí)行完,下面代碼不會執(zhí)行
// dispatch_async_f() 將任務(wù)添加到隊列中砌烁,任務(wù)是函數(shù)非block
/*
dispatch_once:該函數(shù)接收一個dispatch_once用于檢查該代碼塊是否已經(jīng)被調(diào)度的謂詞(是一個長整形筐喳,實際上作為BOOL使用)催式。它還接收一個希望在應(yīng)用的生命周期內(nèi)僅被調(diào)度一次的代碼塊
dispatch_once不僅意味著代碼僅會被運行一次而且還是線程安全的,這就意味著你不需要使用諸如@synchronized之類的來防止使用多個線程或者隊列時不同步的問題
*/
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
// sync會等待block體執(zhí)行完成之后避归,才會去執(zhí)行block體外面的代碼
// dispatch_sync(queue, ^{
//
// NSLog(@"第一個任務(wù)");
//
// });
// NSLog(@"呵呵");
// // async不等待block體執(zhí)行完就去執(zhí)行下面的代碼
// dispatch_async(queue, ^{
//
// NSLog(@"第二個任務(wù)");
//
// });
// NSLog(@"逗比");
// 第一個參數(shù):隊列
// 第二個參數(shù):函數(shù)參數(shù)的內(nèi)容
// 第三個參數(shù):函數(shù)
dispatch_async_f(queue, @"passValue", function);
}
void function(void * str)
{
NSLog(@"這是一個函數(shù),%@",str);
}
- (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的事情
});
}
}];
[task resume];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
-----------------------MyObject.h------------------------
#import <Foundation/Foundation.h>
@interface MyObject : NSObject
+(MyObject *)sharedMyObject;
@end
-----------------------MyObject.m-----------------------
#import "MyObject.h"
static MyObject *object = nil;
@implementation MyObject
+ (MyObject *)sharedMyObject
{
static dispatch_once_t onceToke;
// block中的代碼只會執(zhí)行一次
dispatch_once(&onceToke, ^{
if (object == nil)
{
object = [[MyObject alloc] init];
}
});
return object;
}
@end