多線程方式一:GCD
隊列的3個種類:
- 自建隊列: 分并行/串行
- 全局隊列: 屬于并行隊列, 是系統(tǒng)默認創(chuàng)建的.
- 主隊列: 主線程上的隊列. 串行隊列
1.自建隊列四種
異步:異于當(dāng)前方法所在線程 async
同步:同于當(dāng)前方法所在線程 sync
串行:任務(wù)按順序執(zhí)行 SERIAL
并行:任務(wù)沒有順序,同時執(zhí)行 CONCURRENT
switch (indexPath.section) {
case 0: //異步串行
{
//隊列:任務(wù)是放在隊列中的
//SERIAL串行,序列 CONCURRENT 并行
dispatch_queue_t queue = dispatch_queue_create("串行", DISPATCH_QUEUE_SERIAL);
//隊列放方法,并且規(guī)定方法在哪個線程中執(zhí)行
//把參數(shù)2的方法放到參數(shù)1的隊列中,異步執(zhí)行
dispatch_async(queue, ^{
[self doMethod0];
});
dispatch_async(queue, ^{
[self doMethod1];
});
}
break;
case 1: //異步并行
{
dispatch_queue_t queue = dispatch_queue_create("并行", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
[self doMethod0];
//[self doMethod1];這樣寫,自然就串行了
});
dispatch_async(queue, ^{
[self doMethod1];
});
}
break;
case 2://同步串行
{
[self doMethod0];
[self doMethod1];
return;
dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
[self doMethod0];
});
dispatch_sync(queue, ^{
[self doMethod1];
});
}
break;
case 3: //同步并行
{
dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
[self doMethod0];
});
dispatch_sync(queue, ^{
[self doMethod1];
});
}
break;
default:
break;
}
2.全局隊列和主隊類
- (void)viewDidLoad {
[super viewDidLoad];
//dispatch_get_global_queue獲取全局隊列, 參數(shù)1代表優(yōu)先級, 0是默認優(yōu)先級. 參數(shù)2 無用
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i = 0; i < 10000; i++) {
NSLog(@"%@, %d", [NSThread currentThread], i);
}
//dispatch_get_main_queue() 主隊列 嵌套在全局隊列中 與上方for循環(huán)屬于全局隊列中的一個任務(wù)所以是串行的
dispatch_async(dispatch_get_main_queue(), ^{
//所有與UIKit相關(guān)的操作一定要放到主線程中
UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
v.backgroundColor = [UIColor redColor];
[self.view addSubview:v];
v.center = self.view.center;
});
});
}
barrier(墻) 和分組
- (void)refreshImage{
NSArray *imagePaths = @[@"http://imgstore.cdn.sogou.com/app/a/11220002/7291_pc.jpg", @"http://b.zol-img.com.cn/desk/bizhi/image/3/960x600/1376532627798.jpg", @"http://pic27.nipic.com/20130304/11627225_191300344144_2.jpg"];
//barrier(墻): 墻必須加到自建隊列中.
dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[imagePaths enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSURL *url = [NSURL URLWithString:obj];
dispatch_async(queue, ^{
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *img = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
self.imageViews[idx].image = img;
});
});
}];
//在自建的并行隊列中添加墻, 墻只有在并行隊列中的所有任務(wù)都執(zhí)行結(jié)束后, 才會被推倒
dispatch_barrier_async(queue, ^{
NSLog(@"%@", [NSThread currentThread]);
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
});
return;
//可以把多個子線程放到一個分組中, 通過監(jiān)聽此分組的情況來決定一些操作
dispatch_group_t group = dispatch_group_create();
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
[imagePaths enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSURL *url = [NSURL URLWithString:obj];
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
self.imageViews[idx].image = image;
});
});
}];
//監(jiān)聽分組中的線程都執(zhí)行結(jié)束的時機
dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
NSLog(@"%@", [NSThread currentThread]);
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
});
}
死鎖
- (void)viewDidLoad {
[super viewDidLoad];
/*
死鎖: 在`主線程` 中 調(diào)用 `dispatch_sync` 同步方法
向 `主線程` 中放入一個任務(wù)
dispatch_sync() will not return until the block has finished.
dispatch_sync()這個方法直到block完成以后,才返回.
下方的代碼是 向主線程中插入一個 block. 根據(jù)官方要求 必須是這個block執(zhí)行結(jié)束以后,dispatch_sync()方法才能執(zhí)行完畢.
線程特點: 一個線程同時只能做一件事情, 多個任務(wù)是排隊執(zhí)行的.
看主線程隊列: dispatch_sync()方法 -> 打印block方法
dispatch_sync()的完成需要block方法執(zhí)行完畢.
因為排隊問題 block 必須在 dispatch_sync() 執(zhí)行完畢后再進行!!!
*/
/*
面試問題:下方方法是否有什么問題?
答: 有可能造成死鎖. 因為取決于這個方法執(zhí)行的線程, 只有在主線程才會死鎖.. 說下死鎖的三個關(guān)鍵點.
*/
//dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@".....");
});
//});
//如果解決這個死鎖問題?
//dispatch_async()方法不要內(nèi)部的block完成就可以完成
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@".....");
});
self.view.backgroundColor = [UIColor yellowColor];
}
多線程方式二:NSThread
- (void)redBtnClicked:sender{
NSLog(@"redBtnClicked %@", [NSThread currentThread]);
//在后臺線程執(zhí)行方法
//[self performSelectorInBackground:@selector(wasteTime) withObject:nil];
//方式2:
//[NSThread detachNewThreadSelector:@selector(wasteTime) toTarget:self withObject:nil];
//方式3:
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(wasteTime) object:nil];
thread.name = @"1606";
[thread start];//必須手動開啟
}
//開啟一個新的線程, 在線程中做耗時操作
- (void)wasteTime{
//[NSThread currentThread]: 獲取當(dāng)前方法所在的線程
//number=1 代表主線程, 其他都是子線程
for (int i = 0; i < 10000; i++) {
NSLog(@"%d , %@", i, [NSThread currentThread]);
}
//從子線程回歸主線程,規(guī)避掉線程不安全的操作
[self performSelectorOnMainThread:@selector(changeUI) withObject:nil waitUntilDone:NO];
//waitUntilDone:表示是否要等待changeUI這個方法執(zhí)行結(jié)束以后, 才繼續(xù)執(zhí)行下方代碼
NSLog(@"--------------------");
}
多線程方式三:NSOperation
NSOperation 就是 使用OC語法 對 GCD做的封裝, 效率上略低(可以忽略不計), 繼續(xù)如何選擇 看個人喜好
NSOperationQueue : 隊列
NSOperation : 任務(wù)
任務(wù) 存放在 隊列 中, 隊列 存放在 線程 中
//NSOperation 封裝了GCD之后, 額外增加的易用方法
- (void)runMethod1{
NSOperationQueue *queue = [NSOperationQueue new];
NSBlockOperation *op0 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op0 開始, %@", [NSThread currentThread]);
sleep(2);
NSLog(@"op0 結(jié)束, %@", [NSThread currentThread]);
}];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op1 開始, %@", [NSThread currentThread]);
sleep(2);
NSLog(@"op1 結(jié)束, %@", [NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op2 開始, %@", [NSThread currentThread]);
sleep(2);
NSLog(@"op2 結(jié)束, %@", [NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op3 開始, %@", [NSThread currentThread]);
sleep(2);
NSLog(@"op3 結(jié)束, %@", [NSThread currentThread]);
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op4 開始, %@", [NSThread currentThread]);
sleep(2);
NSLog(@"op4 結(jié)束, %@", [NSThread currentThread]);
}];
NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op5 開始, %@", [NSThread currentThread]);
sleep(2);
NSLog(@"op5 結(jié)束, %@", [NSThread currentThread]);
}];
NSBlockOperation *op6 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op6 開始, %@", [NSThread currentThread]);
sleep(2);
NSLog(@"op6 結(jié)束, %@", [NSThread currentThread]);
}];
NSBlockOperation *op7 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op7 開始, %@", [NSThread currentThread]);
sleep(2);
NSLog(@"op7 結(jié)束, %@", [NSThread currentThread]);
}];
NSBlockOperation *op8 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op8 開始, %@", [NSThread currentThread]);
sleep(2);
NSLog(@"op8 結(jié)束, %@", [NSThread currentThread]);
}];
NSBlockOperation *op9 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"op9 開始, %@", [NSThread currentThread]);
sleep(2);
NSLog(@"op9 結(jié)束, %@", [NSThread currentThread]);
}];
//以為單線程CPU(硬件), 開啟多線程, 是偽多線程. 就是把cpu的線程分很多個時間片, 每個時間片做一件事. 因為速度太快了, 所以顯得跟多線程一樣.
//最佳的線程數(shù)量是3~5個
//NSOperatin就提供了最大并發(fā)數(shù)量的屬性, 此屬性必須在任務(wù)添加到隊列`之前`寫才有用!!
queue.maxConcurrentOperationCount = 3;
//規(guī)定線程依賴. 比如a必須等b線程做完才能進行
//op0 必須等 op7 和 op9 執(zhí)行完畢 才能進行
[op0 addDependency:op7];
[op0 addDependency:op9];
//任務(wù)添加到隊列中之后, 任務(wù)就會自動執(zhí)行
//[queue addOperation:op0];
//waitUntilFinished:參數(shù)表示,下方的打印方法是否要等待隊列中的任務(wù)執(zhí)行結(jié)束之后, 再運行
[queue addOperations:@[op0, op1, op2, op3, op4, op5, op6, op7, op8, op9] waitUntilFinished:NO];
NSLog(@"!@#$##$^^&^&$^@#$");
//取消線程中的所有任務(wù): 已經(jīng)執(zhí)行的任務(wù)無法取消
//[queue cancelAllOperations];
//取消個別任務(wù)
[op2 cancel]; //已經(jīng)執(zhí)行的取消不了
[op9 cancel]; //0依賴9, 9被取消, 0就不再依賴9
//暫停, 已經(jīng)運行的任務(wù) 無法暫停
queue.suspended = YES;
sleep(10);
queue.suspended = NO;
}
//開子線程+回歸主線程
- (void)runMethod0{
//[NSOperationQueue new] 自動開啟子線程
[[NSOperationQueue new] addOperationWithBlock:^{
NSLog(@"[NSOperationQueue new], %@", [NSThread currentThread]);
//回歸主線程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"[NSOperationQueue mainQueue], %@", [NSThread currentThread]);
}];
}];
}