問(wèn)題:項(xiàng)目中用到的多線程技術(shù)
使用dispatch_once_t創(chuàng)建單例
使用dispatch_after延遲做事情
使用dispatch_async異步做一些耗時(shí)或者不影響整理流程的操作,比如清除緩存操作,異步網(wǎng)絡(luò)請(qǐng)求
問(wèn)題:六種組合
|----------------------------------------------------------------------- |
| | 并發(fā)隊(duì)列 | 手動(dòng)創(chuàng)建的串行隊(duì)列 | 主隊(duì)列 |
|----------------------------------------------------------------------- |
|同步(sync) | 沒(méi)有開(kāi)啟新線程 | 沒(méi)有開(kāi)啟新線程 | 沒(méi)有開(kāi)啟新線程 |
| | 串行執(zhí)行任務(wù) | 串行執(zhí)行任務(wù) | 串行執(zhí)行任務(wù) |
|----------------------------------------------------------------------- |
|異步(async)| 有開(kāi)啟新線程 | 有開(kāi)啟新線程 | "沒(méi)有開(kāi)啟"新線程 |
| | 并發(fā)執(zhí)行任務(wù) | 串行執(zhí)行任務(wù) | 串行執(zhí)行任務(wù) |
|-----------------------------------------------------------------------
問(wèn)題:NSOperation
NSOperation的作用
配合使用NSOperation和NSOperationQueue也能實(shí)現(xiàn)多線程編程
NSOperation和NSOperationQueue實(shí)現(xiàn)多線程的具體步驟
先將需要執(zhí)行的操作封裝到一個(gè)NSOperation對(duì)象中
然后將NSOperation對(duì)象添加到NSOperationQueue中
系統(tǒng)會(huì)自動(dòng)將NSOperationQueue中的NSOperation取出來(lái)
將取出的NSOperation封裝的操作放到一條新線程中執(zhí)行
NSOperation的子類
使用NSOperation子類的方式有3種
NSInvocationOperation
NSBlockOperation
自定義子類繼承NSOperation,實(shí)現(xiàn)內(nèi)部相應(yīng)的方法
NSInvocationOperation
創(chuàng)建NSInvocationOperation對(duì)象
- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;
調(diào)用start方法開(kāi)始執(zhí)行操作
- (void)start;
一旦執(zhí)行操作,就會(huì)調(diào)用target的sel方法
注意
默認(rèn)情況下,調(diào)用了start方法后并不會(huì)開(kāi)一條新線程去執(zhí)行操作,而是在當(dāng)前線程同步執(zhí)行操作
只有將NSOperation放到一個(gè)NSOperationQueue中,才會(huì)異步執(zhí)行操作
- (void)viewDidLoad {
[super viewDidLoad];
[self invocationOperation];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)invocationOperation
{
//以下兩步只是在主線程執(zhí)行run
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
[op start];
}
- (void)run
{
NSLog(@"------%@", [NSThread currentThread]);
}
@end
NSBlockOperation
創(chuàng)建NSBlockOperation對(duì)象
+ (id)blockOperationWithBlock:(void (^)(void))block;
通過(guò)addExecutionBlock:方法添加更多的操作
- (void)addExecutionBlock:(void (^)(void))block;
調(diào)用start方法開(kāi)始執(zhí)行操作
- (void)start;
eg:
- (void)viewDidLoad {
[super viewDidLoad];
[self blockOperation];
}
- (void)blockOperation
{
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// 在主線程
NSLog(@"下載1------%@", [NSThread currentThread]);
}];
// 添加額外的任務(wù)(隨機(jī)在主線程執(zhí)行或者隨機(jī)在子線程執(zhí)行)
[op addExecutionBlock:^{
NSLog(@"下載2------%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"下載3------%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"下載4------%@", [NSThread currentThread]);
}];
[op start];
}
NSOperationQueue
NSOperationQueue的作用
NSOperation可以調(diào)用start方法來(lái)執(zhí)行任務(wù),但默認(rèn)是同步執(zhí)行的
如果將NSOperation添加到NSOperationQueue(操作隊(duì)列)中,系統(tǒng)會(huì)自動(dòng)異步執(zhí)行NSOperation中的操作
添加操作到NSOperationQueue中
- (void)addOperation:(NSOperation *)op;
- (void)addOperationWithBlock:(void (^)(void))block;
最大并發(fā)數(shù)的相關(guān)方法
- (NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
取消隊(duì)列的所有操作
- (void)cancelAllOperations;
提示:也可以調(diào)用NSOperation的- (void)cancel方法取消單個(gè)操作
暫停和恢復(fù)隊(duì)列
- (void)setSuspended:(BOOL)b; // YES代表暫停隊(duì)列,NO代表恢復(fù)隊(duì)列。 隊(duì)列暫停時(shí)(當(dāng)前正在執(zhí)行任務(wù)是不會(huì)停止)(將要調(diào)度到線程里任務(wù))
(BOOL)isSuspended;(圖片列表:加載+刷新)
操作優(yōu)先級(jí)
設(shè)置NSOperation在queue中的優(yōu)先級(jí),可以改變操作的執(zhí)行優(yōu)先級(jí)
- (NSOperationQueuePriority)queuePriority;
- (void)setQueuePriority:(NSOperationQueuePriority)p;
優(yōu)先級(jí)的取值
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
操作依賴
NSOperation之間可以設(shè)置依賴來(lái)保證執(zhí)行順序
比如一定要讓操作A執(zhí)行完后,才能執(zhí)行操作B,可以這么寫
[operationB addDependency:operationA]; // 操作B依賴于操作A
可以在不同queue的NSOperation之間創(chuàng)建依賴關(guān)系
操作監(jiān)聽(tīng)
可以監(jiān)聽(tīng)一個(gè)操作的執(zhí)行完畢
- (void (^)(void))completionBlock;
- (void)setCompletionBlock:(void (^)(void))block;
自定義NSOperation
自定義NSOperation的步驟很簡(jiǎn)單
重寫- (void)main方法,在里面實(shí)現(xiàn)想執(zhí)行的任務(wù)
重寫- (void)main方法的注意點(diǎn)
自己創(chuàng)建自動(dòng)釋放池(因?yàn)槿绻钱惒讲僮?,無(wú)法訪問(wèn)主線程的自動(dòng)釋放池)
經(jīng)常通過(guò)- (BOOL)isCancelled方法檢測(cè)操作是否被取消,對(duì)取消做出響應(yīng)
針對(duì)上面的自定義NSOperation和NSOperationQueue有如下的demo
首先自定義一個(gè)NSOperation,即新建一個(gè)類繼承于NSOperation
.h
#import <Foundation/Foundation.h>
@interface XMGOperation : NSOperation
@end
.m
#import "XMGOperation.h"
@implementation XMGOperation
/**
* 需要執(zhí)行的任務(wù) XMGOperation自定義的方法放在這里面,重寫main
*/
- (void)main
{
for (NSInteger i = 0; i<1000; i++) {
//確保當(dāng)前線程取消后能立即生效,視情況而定
if (self.isCancelled) return;
NSLog(@"download1 -%zd-- %@", i, [NSThread currentThread]);
}
//確保當(dāng)前耗時(shí)線程取消后能生效,不執(zhí)行download2,download3
if (self.isCancelled) return;
for (NSInteger i = 0; i<1000; i++) {
NSLog(@"download2 -%zd-- %@", i, [NSThread currentThread]);
}
if (self.isCancelled) return;
for (NSInteger i = 0; i<1000; i++) {
NSLog(@"download3 -%zd-- %@", i, [NSThread currentThread]);
}
if (self.isCancelled) return;
}
@end
演示三種創(chuàng)建operation場(chǎng)景,并添加到NSOperationQueue中
- (void)operationQueue1
{
// 創(chuàng)建隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 創(chuàng)建操作(任務(wù))
// 創(chuàng)建NSInvocationOperation
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download1) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download2) object:nil];
// 創(chuàng)建NSBlockOperation ,這個(gè)依舊子線程
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download3 --- %@", [NSThread currentThread]);
}];
[op3 addExecutionBlock:^{
NSLog(@"download4 --- %@", [NSThread currentThread]);
}];
[op3 addExecutionBlock:^{
NSLog(@"download5 --- %@", [NSThread currentThread]);
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download6 --- %@", [NSThread currentThread]);
}];
// 創(chuàng)建自定義的operation對(duì)象 ,類名為XMGOperation,
XMGOperation *op5 = [[XMGOperation alloc] init];
// 添加任務(wù)到隊(duì)列中
[queue addOperation:op1]; // [op1 start] ,添加到隊(duì)列中去,自動(dòng)默認(rèn)start
[queue addOperation:op2]; // [op2 start]
[queue addOperation:op3]; // [op3 start]
[queue addOperation:op4]; // [op4 start]
[queue addOperation:op5]; // [op5 start]
}
- (void)download1
{
NSLog(@"download1 --- %@", [NSThread currentThread]);
}
- (void)download2
{
NSLog(@"download2 --- %@", [NSThread currentThread]);
}
演示不用創(chuàng)建Blockoperation場(chǎng)景,直接給NSOperationQueue添加block
- (void)operationQueue2
{
// 創(chuàng)建隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
#warning 這兩步可直接簡(jiǎn)化為給NSOperationQueue添加block
// 創(chuàng)建操作
// NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
// NSLog(@"download1 --- %@", [NSThread currentThread]);
// }];
// 添加操作到隊(duì)列中
// [queue addOperation:op1];
//上面兩句作用 等同于 addOperationWithBlock
#warning 也就是下面這句 addOperationWithBlock
[queue addOperationWithBlock:^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"download2 --- %@", [NSThread currentThread]);
}];
}
演示NSOperationQueue并發(fā)數(shù),設(shè)置為1直接就成串行了
- (void)opetationQueue3
{
// 創(chuàng)建隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 設(shè)置最大并發(fā)操作數(shù)
// queue.maxConcurrentOperationCount = 2;
queue.maxConcurrentOperationCount = 1; // 就變成了子線程的串行隊(duì)列,
// 添加操作
[queue addOperationWithBlock:^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"download2 --- %@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"download3 --- %@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"download4 --- %@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"download5 --- %@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"download6 --- %@", [NSThread currentThread]);
}];
}
演示取消和暫停
- (void)viewDidLoad {
[super viewDidLoad];
// 創(chuàng)建隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 設(shè)置最大并發(fā)操作數(shù)
queue.maxConcurrentOperationCount = 1; // 就變成了串行隊(duì)列
// 添加操作
[queue addOperationWithBlock:^{
// NSLog(@"download1 --- %@", [NSThread currentThread]);
// [NSThread sleepForTimeInterval:1.0];
for (NSInteger i = 0; i<5000; i++) {
NSLog(@"download1 -%zd-- %@", i, [NSThread currentThread]);
}
}];
[queue addOperationWithBlock:^{
// NSLog(@"download2 --- %@", [NSThread currentThread]);
// [NSThread sleepForTimeInterval:1.0];
for (NSInteger i = 0; i<1000; i++) {
NSLog(@"download2 --- %@", [NSThread currentThread]);
}
}];
[queue addOperationWithBlock:^{
// NSLog(@"download3 --- %@", [NSThread currentThread]);
// [NSThread sleepForTimeInterval:1.0];
for (NSInteger i = 0; i<1000; i++) {
NSLog(@"download3 --- %@", [NSThread currentThread]);
}
}];
//添加自定義的Operation,在里面阻斷外界暫停和取消操作
[queue addOperation:[[XMGOperation alloc] init]];
self.queue = queue;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
//演示一 線程的暫停和繼續(xù)
// 如果在線程里做耗時(shí)操作,執(zhí)行到一半,暫停是暫時(shí)不會(huì)執(zhí)行的,直到這個(gè)耗時(shí)線程做完會(huì)暫停
// if (self.queue.isSuspended) {
// // 恢復(fù)隊(duì)列,繼續(xù)執(zhí)行
// self.queue.suspended = NO;
// } else {
// // 暫停(掛起)隊(duì)列,暫停執(zhí)行
// self.queue.suspended = YES;
// }
//演示二 線程的取消
//把線程取消,直接消亡
[self.queue cancelAllOperations];
//eg:NSOperationQueue線程的暫停和取消功能,如果這個(gè)線程正在做一個(gè)耗時(shí)操作(比如遍歷),暫停和取消會(huì)等到這個(gè)線程結(jié)束,才會(huì)結(jié)束queue,所以為了避免這種情況,自定義一個(gè)NSOperation(比如自己定義的XMGOperation),在耗時(shí)操作里監(jiān)聽(tīng)取消和暫停狀態(tài),就是一個(gè)很好地將解決辦法
}
演示線程依賴
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download1----%@", [NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download2----%@", [NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download3----%@", [NSThread currentThread]);
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
for (NSInteger i = 0; i<10; i++) {
NSLog(@"download4----%@", [NSThread currentThread]);
}
}];
NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"download5----%@", [NSThread currentThread]);
}];
//op5線程完成以后調(diào)用completionBlock
op5.completionBlock = ^{
NSLog(@"op5執(zhí)行完畢---%@", [NSThread currentThread]);
};
// 設(shè)置依賴 (再添加之前設(shè)置依賴)
[op3 addDependency:op1];
[op3 addDependency:op2];
[op3 addDependency:op4];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue addOperation:op4];
[queue addOperation:op5];
}
線程通信
NSThread線程通信
// 回到主線程,顯示圖片
//waitUntilDone為YES意味著當(dāng)前方法會(huì)卡主,直到完成才會(huì)往下走
[self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];
// [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
// [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];
NSOperation的線程通信
// 回到主線程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
}];
GCD的線程通信
// 回到主線程
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
GCD的一些函數(shù)
GCD的簡(jiǎn)單使用
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self syncConcurrent];
}
/**
* 同步函數(shù) + 主隊(duì)列:(任務(wù)會(huì)卡住)
*/
- (void)syncMain
{
NSLog(@"syncMain ----- begin");
// 1.獲得主隊(duì)列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.將任務(wù)加入隊(duì)列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
NSLog(@"syncMain ----- end");
}
/**
* 異步函數(shù) + 主隊(duì)列:只在主線程中執(zhí)行任務(wù) (這個(gè)特例說(shuō)明了異步函數(shù)可以開(kāi)啟新線程,但并不是絕對(duì)可以開(kāi)啟新線程)
*/
- (void)asyncMain
{
// 1.獲得主隊(duì)列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.將任務(wù)加入隊(duì)列
dispatch_async(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
}
/**
* 同步函數(shù) + 串行隊(duì)列:不會(huì)開(kāi)啟新的線程,在當(dāng)前線程執(zhí)行任務(wù)。任務(wù)是串行的,執(zhí) 任務(wù),再執(zhí)行下一個(gè)任務(wù)
*/
- (void)syncSerial
{
// 1.創(chuàng)建串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_SERIAL);
// 2.將任務(wù)加入隊(duì)列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
}
/**
* 異步函數(shù) + 串行隊(duì)列:會(huì)開(kāi)啟新的線程,但是任務(wù)是串行的,執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)
*/
- (void)asyncSerial
{
// 1.創(chuàng)建串行隊(duì)列(串行隊(duì)列只能創(chuàng)建)
// DISPATCH_QUEUE_SERIAL 和 NULL 都會(huì)是串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_SERIAL);
// dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", NULL);
// 2.將任務(wù)加入隊(duì)列
dispatch_async(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
}
/**
* 同步函數(shù) + 并發(fā)隊(duì)列:不會(huì)開(kāi)啟新的線程(在主線程做)
*/
- (void)syncConcurrent
{
// 1.獲得全局的并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.將任務(wù)加入隊(duì)列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
NSLog(@"syncConcurrent--------end");
}
/**
* 異步函數(shù) + 并發(fā)隊(duì)列:可以同時(shí)開(kāi)啟多條線程
*/
- (void)asyncConcurrent
{
// 1.0創(chuàng)建一個(gè)并發(fā)隊(duì)列
// label : 相當(dāng)于隊(duì)列的名字 "com.520it.queue"
// DISPATCH_QUEUE_CONCURRENT 隊(duì)列類型,并發(fā)隊(duì)列
// dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_CONCURRENT);
// 1.1直接獲得全局的并發(fā)隊(duì)列
//DISPATCH_QUEUE_PRIORITY_DEFAULT優(yōu)先級(jí),作用:優(yōu)先執(zhí)行順序
//默認(rèn)傳0
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.將任務(wù)加入隊(duì)列
dispatch_async(queue, ^{
for (NSInteger i = 0; i<10; i++) {
NSLog(@"1-----%@", [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<10; i++) {
NSLog(@"2-----%@", [NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<10; i++) {
NSLog(@"3-----%@", [NSThread currentThread]);
}
});
NSLog(@"asyncConcurrent--------end");
// dispatch_release(queue); 現(xiàn)在GCD的create現(xiàn)在不需要釋放了
}
@end
隊(duì)列組
在使用GCD進(jìn)行任務(wù)操作時(shí),有時(shí)會(huì)希望若干個(gè)任務(wù)執(zhí)行之間有先后執(zhí)行的依賴關(guān)系。
eg:
// 創(chuàng)建隊(duì)列組
dispatch_group_t group = dispatch_group_create();
// 創(chuàng)建并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("my_queue", DISPATCH_QUEUE_CONCURRENT);
// 添加異步任務(wù)
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任務(wù)1-%@", [NSThread currentThread]);
}
});
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任務(wù)2-%@", [NSThread currentThread]);
}
});
//等前面的任務(wù)執(zhí)行完畢后,會(huì)自動(dòng)執(zhí)行這個(gè)任務(wù)
dispatch_group_notify(group, queue, ^{
dispatch_async(dispatch_get_main_queue(), ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任務(wù)3-%@", [NSThread currentThread]);
}
});
});
/*下面這是大牛講解時(shí)
//等前面的任務(wù)執(zhí)行完畢后,會(huì)自動(dòng)執(zhí)行這個(gè)任務(wù)
dispatch_group_notify(group, dispatch_get_main_queue, ^{
dispatch_async(dispatch_get_main_queue(), ^{
for (int i = 0; i < 5; i++) {
NSLog(@"任務(wù)3-%@", [NSThread currentThread]);
}
});
});
*/
dispatch_set_target_queue
用法一:指定優(yōu)先級(jí)
?
用法二:控制隊(duì)列執(zhí)行階層
如果在多個(gè)SERIAL Dispatch Queue中用dispatch_set_target_queue函數(shù)制定目標(biāo)為某一個(gè)SERIAL Dispatch Queue,那么原本應(yīng)并行執(zhí)行的多個(gè)SERIAL Dispatch Queue,在目標(biāo)SERIAL Dispatch Queue上只能同時(shí)執(zhí)行一個(gè)處理。如下:
//1.創(chuàng)建目標(biāo)隊(duì)列
dispatch_queue_t targetQueue = dispatch_queue_create("test.target.queue", DISPATCH_QUEUE_SERIAL);
//2.創(chuàng)建3個(gè)串行隊(duì)列
dispatch_queue_t queue1 = dispatch_queue_create("test.1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("test.2", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue3 = dispatch_queue_create("test.3", DISPATCH_QUEUE_SERIAL);
//3.將3個(gè)串行隊(duì)列分別添加到目標(biāo)隊(duì)列
dispatch_set_target_queue(queue1, targetQueue);
dispatch_set_target_queue(queue2, targetQueue);
dispatch_set_target_queue(queue3, targetQueue);
dispatch_async(queue1, ^{
NSLog(@"1 in");
[NSThread sleepForTimeInterval:3.f];
NSLog(@"1 out");
});
dispatch_async(queue2, ^{
NSLog(@"2 in");
[NSThread sleepForTimeInterval:2.f];
NSLog(@"2 out");
});
dispatch_async(queue3, ^{
NSLog(@"3 in");
[NSThread sleepForTimeInterval:1.f];
NSLog(@"3 out");
});
打印結(jié)果:
2019-05-06 15:52:36.025069+0800 Test[4273:498926] 1 in
2019-05-06 15:52:39.029506+0800 Test[4273:498926] 1 out
2019-05-06 15:52:39.029782+0800 Test[4273:498926] 2 in
2019-05-06 15:52:41.034457+0800 Test[4273:498926] 2 out
2019-05-06 15:52:41.034623+0800 Test[4273:498926] 3 in
2019-05-06 15:52:42.037019+0800 Test[4273:498926] 3 out
//如上,如果不設(shè)置targetQueue目標(biāo)隊(duì)列,那么queue1queue2queue3亂序跑。
dispatch_after
下面代碼3秒之后執(zhí)行,可以是任何線程
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3*NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{
NSLog(@"waited at east three seconds.");
});
dispatch_group_notify
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
NSLog(@"1");
});
dispatch_group_async(group, queue, ^{
NSLog(@"2");
});
dispatch_group_async(group, queue, ^{
NSLog(@"3");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"done");
});
dispatch_group_wait
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
NSLog(@"1");
});
dispatch_group_async(group, queue, ^{
NSLog(@"2");
sleep(1.0);
});
dispatch_group_async(group, queue, ^{
NSLog(@"3");
});
//DISPATCH_TIME_FOREVER類型也是dispatch_time_t型
//注意點(diǎn),一旦調(diào)用dispatch_group_wait函數(shù),該函數(shù)就處于調(diào)用狀態(tài)而不返回。即是當(dāng)前線程已經(jīng)卡在這兒,不向后執(zhí)行,必須等待前面任務(wù)處理有結(jié)果或者自己設(shè)定的dispatch_time_t時(shí)間結(jié)束。
BOOL result = dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
if (result == 0) {//返回值為0,那么全部處理執(zhí)行結(jié)束
NSLog(@"done");
}else{//如果時(shí)間過(guò),但是前面的任務(wù)還沒(méi)有跑完,會(huì)走到這兒
NSLog(@"任務(wù)仍在執(zhí)行");
};
dispatch_barrier_async
//柵欄的隊(duì)列不能使全局的并發(fā),要是手動(dòng)創(chuàng)建的并發(fā) (待考證)
dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----2-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----3-----%@", [NSThread currentThread]);
});
//barrier柵欄效果
//官方解釋:前面的執(zhí)行完才到這,這個(gè)執(zhí)行完才執(zhí)行后面(★待考證)
dispatch_barrier_async(queue, ^{
NSLog(@"----barrier-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----4-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----5-----%@", [NSThread currentThread]);
});
dispatch_apply
該函數(shù)按指定的次數(shù)將指定的block追加到指定的Dispatch Queue中,并等待全部處理結(jié)束。推薦在dispatch_async中非同步的執(zhí)行dispatch_apply函數(shù)。
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"----------------主線程開(kāi)始------------");
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async( queue, ^{
NSLog(@"子線程%@",[NSThread currentThread]);
dispatch_apply(3, queue, ^(size_t index) {
NSLog(@"追加第%zu次任務(wù)",index);
});
NSLog(@"done");
//可以回到主線程進(jìn)行通信了
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"刷新界面");
});
});
NSLog(@"----------------主線程繼續(xù)------------");
}
打印結(jié)果:
2019-05-07 10:16:06.001245+0800 Test[2820:134508] ----------------主線程開(kāi)始------------
2019-05-07 10:16:06.001436+0800 Test[2820:134508] ----------------主線程繼續(xù)------------
2019-05-07 10:16:06.001476+0800 Test[2820:134543] 子線程<NSThread: 0x600000c20a00>{number = 3, name = (null)}
2019-05-07 10:16:06.001567+0800 Test[2820:134543] 追加第0次任務(wù)
2019-05-07 10:16:06.001637+0800 Test[2820:134543] 追加第1次任務(wù)
2019-05-07 10:16:06.001700+0800 Test[2820:134543] 追加第2次任務(wù)
2019-05-07 10:16:06.001761+0800 Test[2820:134543] done
2019-05-07 10:16:06.009918+0800 Test[2820:134508] 刷新界面
dispatch_suspend和dispatch_resume
線程的掛起和恢復(fù)。dispatch_suspend并不會(huì)立即暫停正在運(yùn)行的block,而是在當(dāng)前block執(zhí)行完成后,暫停后續(xù)的block執(zhí)行。
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t queue = dispatch_queue_create("com.test.gcd", DISPATCH_QUEUE_SERIAL);
//提交第一個(gè)block,延時(shí)2秒打印。
dispatch_async(queue, ^{
sleep(2);
NSLog(@"After 2 seconds...");
});
//提交第二個(gè)block,也是延時(shí)2秒打印
dispatch_async(queue, ^{
sleep(2);
NSLog(@"After 2 seconds again...");
});
//延時(shí)一秒
NSLog(@"sleep 1 second...");
sleep(1);
//掛起隊(duì)列
NSLog(@"suspend...");
dispatch_suspend(queue);
//延時(shí)10秒
NSLog(@"sleep 10 second...");
sleep(10);
//恢復(fù)隊(duì)列
NSLog(@"resume...");
dispatch_resume(queue);
}
問(wèn)題:GCD和nsopreation什么場(chǎng)景使用哪個(gè)好點(diǎn)
GCD主要特點(diǎn):
1)GCG 是iOS4.0推出的,主要針對(duì)多核CPU 做了優(yōu)化
2)GCD是 C 語(yǔ)言的技術(shù)
3)GCD 提供了一些 NSOperation 不具備的功能,比如一次性執(zhí)行(創(chuàng)建單例),延遲執(zhí)行,調(diào)度組.
NSOperation 特點(diǎn):
- NSOperation 是 iOS2.0后推出的,iOS4.0之后重寫了NSOperation.
- NSOperation 將操作(異步的任務(wù))添加到隊(duì)列(并發(fā)隊(duì)列),就會(huì)執(zhí)行制定操作的函數(shù).
- NSOperation里可以方便的設(shè)置操作:
1??最大并發(fā)數(shù)
2??隊(duì)列的暫停/繼續(xù)
3??取消所有的操作
4??指定操作之間的依賴關(guān)系(GCD可以用同步實(shí)現(xiàn))
**使用NSOperation 需要注意幾點(diǎn)點(diǎn): - 注意避免產(chǎn)生循環(huán)依賴
- 要先設(shè)置依賴關(guān)系,然后添加到隊(duì)列
GCD 和 NSOperation的區(qū)別主要表現(xiàn)在以下幾方面:
GCD是一套 C 語(yǔ)言API,執(zhí)行和操作簡(jiǎn)單高效,因此NSOperation底層也通過(guò)GCD實(shí)現(xiàn),這是他們之間最本質(zhì)的區(qū)別.因此如果希望自定義任務(wù),建議使用NSOperation;
依賴關(guān)系,NSOperation可以設(shè)置操作之間的依賴(可以跨隊(duì)列設(shè)置),GCD無(wú)法設(shè)置依賴關(guān)系,不過(guò)可以通過(guò)同步來(lái)實(shí)現(xiàn)這種效果;
KVO(鍵值對(duì)觀察),NSOperation容易判斷操作當(dāng)前的狀態(tài)(是否執(zhí)行,是否取消等),對(duì)此GCD無(wú)法通過(guò)KVO進(jìn)行判斷;
(指的應(yīng)該是:NSOperationQueue中使用了KVO,當(dāng)NSOperation對(duì)象狀態(tài)變化(finished,canceled等)時(shí),觀察者可以知道變化,從而做出相應(yīng)的處理(移除已完成的等等))優(yōu)先級(jí),NSOperation可以設(shè)置自身的優(yōu)先級(jí),但是優(yōu)先級(jí)高的不一定先執(zhí)行,GCD只能設(shè)置隊(duì)列的優(yōu)先級(jí),如果要區(qū)分block任務(wù)的優(yōu)先級(jí),需要很復(fù)雜的代碼才能實(shí)現(xiàn);
繼承,NSOperation是一個(gè)抽象類.實(shí)際開(kāi)發(fā)中常用的是它的兩個(gè)子類:NSInvocationOperation和NSBlockOperation,同樣我們可以自定義NSOperation,GCD執(zhí)行任務(wù)可以自由組裝,沒(méi)有繼承那么高的代碼復(fù)用度;
效率,直接使用GCD效率確實(shí)會(huì)更高效,NSOperation會(huì)多一點(diǎn)開(kāi)銷,但是通過(guò)NSOperation可以獲得依賴,優(yōu)先級(jí),繼承,鍵值對(duì)觀察這些優(yōu)勢(shì),相對(duì)于多的那么一點(diǎn)開(kāi)銷確實(shí)很劃算,魚和熊掌不可得兼,取舍在于開(kāi)發(fā)者自己;
7)可以隨時(shí)取消準(zhǔn)備執(zhí)行的任務(wù)(已經(jīng)在執(zhí)行的不能取消),GCD沒(méi)法停止已經(jīng)加入queue 的 block(雖然也能實(shí)現(xiàn),但是需要很復(fù)雜的代碼)
基于GCD簡(jiǎn)單高效,更強(qiáng)的執(zhí)行能力,操作不太復(fù)雜的時(shí)候,優(yōu)先選用GCD;而比較復(fù)雜的任務(wù)可以自己通過(guò)NSOperation實(shí)現(xiàn).
問(wèn)題:atomic
atomic用于保證屬性setter、getter的原子性操作,相當(dāng)于在setter和getter內(nèi)部加了線程同步的鎖,從而保證了setter和getter內(nèi)部是線程同步的。
可以參考源碼objc4的objc-accessors.mm
首先看設(shè)置屬性
void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy)
{
bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY);
bool mutableCopy = (shouldCopy == MUTABLE_COPY);
reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy);
}
點(diǎn)擊查看reallySetProperty方法
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
{
if (offset == 0) {
object_setClass(self, newValue);
return;
}
id oldValue;
id *slot = (id*) ((char*)self + offset);
if (copy) {
newValue = [newValue copyWithZone:nil];
} else if (mutableCopy) {
newValue = [newValue mutableCopyWithZone:nil];
} else {
if (*slot == newValue) return;
newValue = objc_retain(newValue);
}
if (!atomic) {
oldValue = *slot;
*slot = newValue;
} else { //如果是atomic,可以看到加了一個(gè)spinlock_t自旋鎖
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
}
objc_release(oldValue);
}
再看get屬性
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
if (offset == 0) {
return object_getClass(self);
}
// Retain release world
id *slot = (id*) ((char*)self + offset);
if (!atomic) return *slot;
// Atomic retain release world
spinlock_t& slotlock = PropertyLocks[slot]; //如果是atomic,可以看到加了一個(gè)spinlock_t自旋鎖
slotlock.lock();
id value = objc_retain(*slot);
slotlock.unlock();
// for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
return objc_autoreleaseReturnValue(value);
}
它并不能保證"使用屬性"的過(guò)程是線程安全的。只是保證了set和get方法內(nèi)部是線程安全的,但是并不能保證屬性的過(guò)程是線程安全的。
說(shuō)atomic并不能保證使用屬性的過(guò)程是線程安全的,
舉個(gè)例子
1.對(duì)于NSArray類型 @property(atomic)NSArray *array我們用atomic修飾,數(shù)組的添加和刪除并不是線程安全的,這是因?yàn)閿?shù)組比較特殊,我們要分成兩部分考慮,一部分是&array也就是這個(gè)數(shù)組本身,另一部分是他所指向的內(nèi)存部分。atomic限制的只是&array部分,對(duì)于它指向的對(duì)象沒(méi)有任何限制。
eg:
MJPerson *p = [[MJPerson alloc] init];//初始化類,類里面之前生命了一個(gè)可變數(shù)組dataArray屬性
p.dataArray = [NSMutableArray array];//初始化dataArray,這里面p.dataArray是線程安全的,因?yàn)檎{(diào)用了set方法
[p.dataArray addObject:@"1"]; //這里面p.dataArray是線程安全的,因?yàn)檎{(diào)用了get方法,但是addObject卻沒(méi)有安全管控
[p.dataArray addObject:@"2"];
2、比如如果線程 A 調(diào)了 getter,與此同時(shí)線程 B 、線程 C 都調(diào)了 setter——那最后線程 A get 到的值,3種都有可能:可能是 B、C set 之前原始的值,也可能是 B set 的值,也可能是 C set 的值。同時(shí),最終這個(gè)屬性的值,可能是 B set 的值,也有可能是 C set 的值。
如下
@property (atomic, assign) NSInteger intA;
- (void)viewDidLoad {
[super viewDidLoad];
//開(kāi)啟一個(gè)線程對(duì)intA的值+1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0;i < 1000;i ++){
self.intA = self.intA + 1;
}
NSLog(@"首先加一:intA : %ld",(long)self.intA);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"首先加一后:intA : %ld",(long)self.intA);
});
//開(kāi)啟一個(gè)線程對(duì)intA的值+1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0;i < 1000;i ++){
self.intA = self.intA + 1;
}
NSLog(@"其次加一:intA : %ld",(long)self.intA);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"其次加一后:intA : %ld",(long)self.intA);
});
}
@end
打?。?因?yàn)槎际钱惒?,打印順序并不固?
2021-02-03 17:39:09.929929+0800 OC_Test[6945:206188] 首先加一:intA : 1000
2021-02-03 17:39:09.930341+0800 OC_Test[6945:206184] 其次加一:intA : 2000
2021-02-03 17:39:09.930385+0800 OC_Test[6945:206186] 其次加一后:intA : 2000
2021-02-03 17:39:09.931724+0800 OC_Test[6945:206189] 首先加一后:intA : 0
atomic:原子性
nonatomic:非原子性
聲明屬性時(shí)不寫的話默認(rèn)就是atomic
忙等
自旋鎖(Spinlock)是一種忙等待鎖,線程反復(fù)檢查鎖變量是否可用,不會(huì)掛起,避免了進(jìn)程上下文的調(diào)度開(kāi)銷,適合阻塞很短時(shí)間的場(chǎng)合。當(dāng)然也就不適合單CPU單線程上使用。
問(wèn)題:dispatch_sync
意味著將指定的block“同步”追加到指定的Dispatch Queue中,在追加block結(jié)束之前,dispatch_sync函數(shù)會(huì)一直等待。可以認(rèn)為是簡(jiǎn)單版的dispatch_group_wait
由于sync特性,所以使用sync函數(shù)往當(dāng)前"串行"隊(duì)列中添加任務(wù),會(huì)卡住當(dāng)前的串行隊(duì)列(產(chǎn)生死鎖)
死鎖的原因是:隊(duì)列引起的循環(huán)等待。由于當(dāng)前串行隊(duì)列執(zhí)行到dispatch_sync處等待dispatch_sync的返回,dispatch_sync必須等待block執(zhí)行完畢后才能返回,由于當(dāng)前隊(duì)列是串行隊(duì)列,先進(jìn)先出,但是我們通過(guò)dispatch_sync新放入的block位于隊(duì)列后面,現(xiàn)在得不到執(zhí)行,所以鎖住了。
問(wèn)題:以下代碼是在主線程執(zhí)行的,會(huì)不會(huì)產(chǎn)生死鎖?會(huì)!
NSLog(@"執(zhí)行任務(wù)1");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"執(zhí)行任務(wù)2");
});
NSLog(@"執(zhí)行任務(wù)3");
原因:
因?yàn)榘淹降娜蝿?wù)2添加到主隊(duì)列,dispatch_sync要求立馬在當(dāng)前線程同步執(zhí)行任務(wù),走到這一步想要立即執(zhí)行。想要執(zhí)行隊(duì)列里的任務(wù)2,必須要等前面一整個(gè)任務(wù)執(zhí)行完。任務(wù)3在等任務(wù)2,任務(wù)2在等任務(wù)3。
用圖表示為
當(dāng)前線程 | 主隊(duì)列
任務(wù)1 | viewDidLoad
sync | 任務(wù)2
任務(wù)3 |
那么把上述的dispatch_sync換成dispatch_async會(huì)不會(huì)產(chǎn)生死鎖?不會(huì)!
NSLog(@"執(zhí)行任務(wù)1");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
NSLog(@"執(zhí)行任務(wù)2");
});
NSLog(@"執(zhí)行任務(wù)3");
原因:
dispatch_async不要求立馬在當(dāng)前線程同步執(zhí)行任務(wù)
//打印結(jié)果為 執(zhí)行任務(wù)1 執(zhí)行任務(wù)3 執(zhí)行任務(wù)2
問(wèn)題:以下代碼會(huì)不會(huì)產(chǎn)生死鎖?不會(huì)!
-
(void)viewDidLoad {
[super viewDidLoad];
NSLog(@"執(zhí)行任務(wù)1");dispatch_async(dispatch_get_global_queue(0, 0), ^{
//將上面放在主線程會(huì)死鎖的代碼放到子線程里不會(huì)死鎖
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"執(zhí)行任務(wù)2");
});
});NSLog(@"執(zhí)行任務(wù)3");
}
問(wèn)題:以下代碼是在主線程執(zhí)行的,會(huì)不會(huì)產(chǎn)生死鎖?會(huì)!
NSLog(@"執(zhí)行任務(wù)1");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{ // 0
NSLog(@"執(zhí)行任務(wù)2");
dispatch_sync(queue, ^{ // 1
NSLog(@"執(zhí)行任務(wù)3");
});
NSLog(@"執(zhí)行任務(wù)4");
});
NSLog(@"執(zhí)行任務(wù)5");
問(wèn)題:以下代碼是在主線程執(zhí)行的,會(huì)不會(huì)產(chǎn)生死鎖?會(huì)!
NSLog(@"執(zhí)行任務(wù)1");
dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{ // 0
NSLog(@"執(zhí)行任務(wù)2");
dispatch_sync(queue, ^{ // 1
NSLog(@"執(zhí)行任務(wù)3");
});
NSLog(@"執(zhí)行任務(wù)4");
});
NSLog(@"執(zhí)行任務(wù)5");
問(wèn)題:以下代碼是在主線程執(zhí)行的,會(huì)不會(huì)產(chǎn)生死鎖?不會(huì)!雖是同步,但確實(shí)并發(fā)隊(duì)列
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"1");
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"2");
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
// Do any additional setup after loading the view.
}
下面代碼執(zhí)行會(huì)打印什么
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
NSLog(@"1");
[self performSelector:@selector(test) withObject:nil afterDelay:.0];
NSLog(@"3");
//添加這行代碼,啟動(dòng)RunLoop程序才能打印2
//[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
});
}
- (void)test
{
NSLog(@"2");
}
答:會(huì)打印 1 3,但是2不會(huì)打印。因?yàn)閜erformSelector:withObject:afterDelay:這句代碼的本質(zhì)是往Runloop中添加定時(shí)器,而子線程的Runloop在這里沒(méi)有啟動(dòng)。
注意:- (id)performSelector:(SEL)aSelector withObject:(id)object;等不需要Runloop。
下面代碼執(zhí)行會(huì)打印什么
- (void)test
{
NSLog(@"2");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSThread *thread = [[NSThread alloc] initWithBlock:^{
NSLog(@"1");
//啟動(dòng)RunLoop,保住線程。然后程序正常運(yùn)行,打印了2
//[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
//[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}];
[thread start];
[self performSelector:@selector(test) onThread:thread withObject:nil waitUntilDone:YES];
}
答:會(huì)打印 1 ,然后程序崩潰。因?yàn)閳?zhí)行完1,block結(jié)束后線程結(jié)束。
問(wèn)題:信號(hào)量
dispatch_semaphore
semaphore叫做”信號(hào)量”
信號(hào)量的初始值,可以用來(lái)控制線程并發(fā)訪問(wèn)的最大數(shù)量
信號(hào)量的初始值為1,代表同時(shí)只允許1條線程訪問(wèn)資源,保證線程同步
//定義一個(gè)控制最大量的值
int value= 1;
//初始化信號(hào)量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(value);
//執(zhí)行到這句話時(shí),如果信號(hào)量的值<=0,當(dāng)前線程會(huì)進(jìn)入睡眠等待(直到信號(hào)量的值>0);如果信號(hào)量的值>0,就減1,繼續(xù)往下執(zhí)行。
dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);
//讓信號(hào)量的值加1
dispatch_semaphore_signal(semaphore);
信號(hào)量的使用場(chǎng)景
加鎖
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
for (int i = 0; i < 10000; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//臨界區(qū),即待加鎖的代碼區(qū)域
dispatch_semaphore_signal(semaphore);
});
}
在進(jìn)行多線程任務(wù)之前,首先創(chuàng)建一個(gè)計(jì)數(shù)為1的信號(hào)量,這樣可以保證同一時(shí)刻只有一個(gè)線程在訪問(wèn)臨界區(qū)。上面代碼,系統(tǒng)會(huì)同時(shí)開(kāi)啟很多線程想要做遍歷里面的事,但是同時(shí)只有一個(gè)線程能去做臨界區(qū)的事情。
異步任務(wù),同步返回
- (NSArray *)tasksForKeyPath:(NSString *)keyPath {
__block NSArray *tasks = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
//task賦值,代碼有點(diǎn)長(zhǎng),就不貼了
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return tasks;
}
上面是 AFNetworking 的一段代碼,我且稱之為異步去做,同步返回。這段代碼的功能是通過(guò)異步的請(qǐng)求取得鍵路徑為 keyPath 的任務(wù)數(shù)組 tasks,然后同步返回它。
控制線程并發(fā)數(shù)
在 GCD 中,dispatch_async() 異步操作可以產(chǎn)生新的線程,但是方法本身沒(méi)辦法限制線程的最大并發(fā)數(shù),線程的創(chuàng)建和銷毀是由 GCD 底層管理的。
了解 NSOperationQueue 的同學(xué)肯定知道,通過(guò) maxConcurrentOperationCount 屬性可以設(shè)置它的最大并發(fā)數(shù)。那么在GCD中,對(duì)應(yīng)的解決方法就是使用信號(hào)量。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);
for (int i = 0; i < 1000; ++i) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//多線程代碼
dispatch_semaphore_signal(semaphore);
});
}
其實(shí)跟加鎖代碼非常相似,區(qū)別在于,在初始化信號(hào)量時(shí),將計(jì)數(shù)賦值為最大并發(fā)數(shù)。在應(yīng)用場(chǎng)景上,限制線程并發(fā)數(shù)是為了性能考慮,而加鎖是為了安全而考慮。
信號(hào)量和柵欄函數(shù)是鎖嗎
信號(hào)量是屬于很多種加鎖方式的一種,柵欄函數(shù)能實(shí)現(xiàn)讀寫鎖的功能,所以應(yīng)該都屬于鎖。
問(wèn)題:線程池
線程池是一種線程使用模式。 線程過(guò)多會(huì)帶來(lái)調(diào)度開(kāi)銷,進(jìn)而影響緩存局部性和整體性能。 而線程池維護(hù)著多個(gè)線程,等待著監(jiān)督管理者分配可并發(fā)執(zhí)行的任務(wù)。 這避免了在處理短時(shí)間任務(wù)時(shí)創(chuàng)建與銷毀線程的代價(jià)。
線程池的執(zhí)行流程如圖下:

若線程池大小小于核心線程池大小時(shí)
創(chuàng)建線程執(zhí)行任務(wù)
若線程池大小大于等于核心線程池大小時(shí)
1.先判斷線程池工作隊(duì)列是否已滿
2.若沒(méi)滿就將任務(wù)push進(jìn)隊(duì)列
3.若已滿時(shí),且maximumPoolSize>corePoolSize,將創(chuàng)建新的線程來(lái)4.執(zhí)行任務(wù)
反之則交給飽和策略去處理
參數(shù)名代表意義
corePoolSize
線程池的基本大小(核心線程池大?。?/p>
maximumPool
線程池的最大大小
keepAliveTime
線程池中超過(guò)corePoolSize樹木的空閑線程的最大存活時(shí)間
unit
keepAliveTime參數(shù)的時(shí)間單位
workQueue
任務(wù)阻塞隊(duì)列
threadFactory
新建線程的工廠
handler
當(dāng)提交的任務(wù)數(shù)超過(guò)maxmumPoolSize與workQueue之和時(shí),任務(wù)會(huì)交給RejectedExecutionHandler來(lái)處理
上圖的解釋
我們知道,受限于硬件、內(nèi)存和性能,我們不可能無(wú)限制的創(chuàng)建任意數(shù)量的線程,因?yàn)槊恳慌_(tái)機(jī)器允許的最大線程是一個(gè)有界值。也就是說(shuō)ThreadPoolExecutor管理的線程數(shù)量是有界的。線程池就是用這些有限個(gè)數(shù)的線程,去執(zhí)行提交的任務(wù)。然而對(duì)于多用戶、高并發(fā)的應(yīng)用來(lái)說(shuō),提交的任務(wù)數(shù)量非常巨大,一定會(huì)比允許的最大線程數(shù)多很多。為了解決這個(gè)問(wèn)題,必須要引入排隊(duì)機(jī)制,或者是在內(nèi)存中,或者是在硬盤等容量很大的存儲(chǔ)介質(zhì)中。J.U.C提供的ThreadPoolExecutor只支持任務(wù)在內(nèi)存中排隊(duì),通過(guò)BlockingQueue暫存還沒(méi)有來(lái)得及執(zhí)行的任務(wù)。
任務(wù)的管理是一件比較容易的事,復(fù)雜的是線程的管理,這會(huì)涉及線程數(shù)量、等待/喚醒、同步/鎖、線程創(chuàng)建和死亡等問(wèn)題。ThreadPoolExecutor與線程相關(guān)的幾個(gè)成員變量是:keepAliveTime、allowCoreThreadTimeOut、poolSize、corePoolSize、maximumPoolSize,它們共同負(fù)責(zé)線程的創(chuàng)建和銷毀。
corePoolSize:
線程池的基本大小,即在沒(méi)有任務(wù)需要執(zhí)行的時(shí)候線程池的大小,并且只有在工作隊(duì)列滿了的情況下才會(huì)創(chuàng)建超出這個(gè)數(shù)量的線程。這里需要注意的是:在剛剛創(chuàng)建ThreadPoolExecutor的時(shí)候,線程并不會(huì)立即啟動(dòng),而是要等到有任務(wù)提交時(shí)才會(huì)啟動(dòng),除非調(diào)用了prestartCoreThread/prestartAllCoreThreads事先啟動(dòng)核心線程。再考慮到keepAliveTime和allowCoreThreadTimeOut超時(shí)參數(shù)的影響,所以沒(méi)有任務(wù)需要執(zhí)行的時(shí)候,線程池的大小不一定是corePoolSize。
maximumPoolSize:
線程池中允許的最大線程數(shù),線程池中的當(dāng)前線程數(shù)目不會(huì)超過(guò)該值。如果隊(duì)列中任務(wù)已滿,并且當(dāng)前線程個(gè)數(shù)小于maximumPoolSize,那么會(huì)創(chuàng)建新的線程來(lái)執(zhí)行任務(wù)。這里值得一提的是largestPoolSize,該變量記錄了線程池在整個(gè)生命周期中曾經(jīng)出現(xiàn)的最大線程個(gè)數(shù)。為什么說(shuō)是曾經(jīng)呢?因?yàn)榫€程池創(chuàng)建之后,可以調(diào)用setMaximumPoolSize()改變運(yùn)行的最大線程的數(shù)目。
poolSize:
線程池中當(dāng)前線程的數(shù)量,當(dāng)該值為0的時(shí)候,意味著沒(méi)有任何線程,線程池會(huì)終止;同一時(shí)刻,poolSize不會(huì)超過(guò)maximumPoolSize。
https://www.cnblogs.com/frankyou/p/10135212.html
問(wèn)題:異步執(zhí)行ABC之后在執(zhí)行D的正確理解
首先異步執(zhí)行ABC分兩種情況,一種任務(wù)是同步的,一種任務(wù)是網(wǎng)絡(luò)請(qǐng)求,發(fā)送操作是同步的,但是請(qǐng)求到的結(jié)果是異步的。
任務(wù)是同步時(shí)
第一種GCD group
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_async(group, queue, ^{
NSLog(@"同步任務(wù)A");
});
dispatch_group_async(group, queue, ^{
NSLog(@"同步任務(wù)B");
});
dispatch_group_async(group, queue, ^{
NSLog(@"同步任務(wù)C");
});
dispatch_group_notify(group, queue, ^{
NSLog(@"任務(wù)完成執(zhí)行");
});
第二種 dispatch_barrier_async
dispatch_queue_t queue = dispatch_queue_create(0, DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"任務(wù)A");
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)B");
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)C");
});
dispatch_barrier_async(queue, ^{
NSLog(@"阻塞自定義并發(fā)隊(duì)列");
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)D");
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)E");
});
第三種 NSOperation
NSBlockOperation *operatioon1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任務(wù)A");
}];
NSBlockOperation *operatioon2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任務(wù)B");
}];
NSBlockOperation *operatioon3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任務(wù)C");
}];
NSBlockOperation *operatioon4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"任務(wù)D");
}];
[operatioon4 addDependency:operatioon1];
[operatioon4 addDependency:operatioon2];
[operatioon4 addDependency:operatioon3];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperations:@[operatioon1,operatioon2,operatioon3,operatioon4] waitUntilFinished:YES];
NSLog(@"完成之后的操作");
任務(wù)時(shí)異步時(shí)(比如網(wǎng)絡(luò)請(qǐng)求異步任務(wù))
第一種 dispatch_group + semaphore
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_group_async(group, queue, ^{
NSLog(@"同步任務(wù)A");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"網(wǎng)絡(luò)異步任務(wù)一");
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});
dispatch_group_async(group, queue, ^{
NSLog(@"同步任務(wù)B");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.8f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"網(wǎng)絡(luò)異步任務(wù)二");
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});
dispatch_group_async(group, queue, ^{
NSLog(@"同步任務(wù)C");
});
dispatch_group_async(group, queue, ^{
NSLog(@"同步任務(wù)D");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.5f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"網(wǎng)絡(luò)異步任務(wù)四");
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});
dispatch_group_notify(group, queue, ^{
NSLog(@"任務(wù)完成執(zhí)行");
});
第二種 dispatch_group_enter 和 dispatch_group_leave
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
NSLog(@"同步任務(wù)A");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"網(wǎng)絡(luò)異步任務(wù)一");
dispatch_group_leave(group);
});
});
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
NSLog(@"同步任務(wù)B");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.8f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"網(wǎng)絡(luò)異步任務(wù)二");
dispatch_group_leave(group);
});
});
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
NSLog(@"同步任務(wù)C");
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
NSLog(@"同步任務(wù)D");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.5f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"網(wǎng)絡(luò)異步任務(wù)四");
dispatch_group_leave(group);
});
});
dispatch_group_notify(group, queue, ^{
NSLog(@"任務(wù)完成執(zhí)行");
});