本文內(nèi)容:
如何使用NSOperation創(chuàng)建任務(wù)、設(shè)置依賴、設(shè)置優(yōu)先級(jí)
如何使用NSOperationQueue創(chuàng)建隊(duì)列,添加任務(wù)到隊(duì)列、控制隊(duì)列串行、并行執(zhí)行任務(wù)
線程間如何通信
如何保證線程安全
iOS多線程demo地址
上文說(shuō)到iOS 多線程-GCD
這篇文章來(lái)講講NSOperation + NSOperationQueue
NSOperation:基于OC語(yǔ)言的API,底層是GCD,增加了一些簡(jiǎn)單易用的功能,面向?qū)ο蟛僮?,線程的生命周期系統(tǒng)自動(dòng)管理,在開(kāi)發(fā)中經(jīng)常使用。
1. NSOperation & NSOperationQueue
NSOperation:執(zhí)行的操作,也就是任務(wù)的意思,不過(guò)NSOperation:是一個(gè)抽象基類,不能直接使用。
NSOperation兩種使用方式:
- 使用系統(tǒng)定義的
NSInvocationOperation - 使用系統(tǒng)定義的
NSBlockOperation - 自定義類繼承
NSOperation
任務(wù)的狀態(tài)分為以下幾種:
- ready : 就緒
- cancelled : 取消 (只能取消沒(méi)有開(kāi)始的任務(wù))
- executing : 正在執(zhí)行
- finished : 完成
- asynchronous : 并發(fā)還是非并發(fā)
NSOperationQueue: 存放任務(wù)的隊(duì)列。
隊(duì)列分為:
- 主隊(duì)列:添加到主隊(duì)列的任務(wù),只能在主線程執(zhí)行,除
addExecutionBlock添加的額外任務(wù),任務(wù)可能在其他線程執(zhí)行(下面有證明); - 其他隊(duì)列:添加到其他隊(duì)列的任務(wù)在子線程執(zhí)行。
常用方法:
NSOperation使用addDependency添加依賴, 控制任務(wù)是否進(jìn)入就緒狀態(tài),從而控制任務(wù)執(zhí)行順序。
NSOperationQueue使用addOperation添加任務(wù)到隊(duì)列。
NSOperationQueue設(shè)置 maxConcurrentOperationCount(最大操作并發(fā)數(shù)),用來(lái)控制一個(gè)隊(duì)列中有多少個(gè)任務(wù)同時(shí)進(jìn)行,而不是并發(fā)線程的數(shù)量。
NSOperation使用cancel 取消當(dāng)前任務(wù)
NSOperationQueue使用cancelAllOperations取消當(dāng)前隊(duì)列的任務(wù)
NSOperationQueue使用setSuspended設(shè)置任務(wù)的暫停和恢復(fù)
注意:
- 任務(wù)、隊(duì)列的取消并不代表可以將當(dāng)前的操作立即取消,而是當(dāng)前的操作執(zhí)行完畢之后不再執(zhí)行新的操作
- 暫停和取消的區(qū)別就在于:暫停操作之后還可以恢復(fù)操作,繼續(xù)向下執(zhí)行;而取消操作之后,所有的操作就清空了,無(wú)法再接著執(zhí)行剩下的操作。
2.使用步驟
- 創(chuàng)建任務(wù)
- 創(chuàng)建隊(duì)列
- 將任務(wù)添加到隊(duì)列中執(zhí)行
2.1創(chuàng)建任務(wù)
2.1.1 NSInvocationOperation
使用NSInvocationOperation創(chuàng)建任務(wù),使用start方式開(kāi)始任務(wù)
- (void)clickNSOperation{
NSLog(@"主線程");
// 1、NSInvocationOperation
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(invocationAction) object:nil];
[invocationOperation start];
NSLog(@"end----");
}
- (void)invocationAction{
for (NSInteger index = 0; index < 3; index ++) {
NSLog(@"invocation ==== %ld",(long)index);
[NSThread sleepForTimeInterval:1.0];
}
}
輸出結(jié)果:
- 在主線程中執(zhí)行(因?yàn)槭窃谥骶€程中調(diào)用)
-
end最后輸出,start方式執(zhí)行任務(wù)是同步執(zhí)行,睡眠時(shí)阻塞當(dāng)前線程。
2019-01-03 15:59:44.044995+0800 Thread[5045:65704] 主線程
2019-01-03 15:59:44.045577+0800 Thread[5045:65704] invocation ==== 0
2019-01-03 15:59:45.046159+0800 Thread[5045:65704] invocation ==== 1
2019-01-03 15:59:46.047736+0800 Thread[5045:65704] invocation ==== 2
2019-01-03 15:59:47.048575+0800 Thread[5045:65704] end----
在主線程調(diào)用就在主線程執(zhí)行,那在子線程中調(diào)用會(huì)如何呢?
- (void)clickNSOperation{
NSLog(@"主線程");
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// 1、NSInvocationOperation
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(invocationAction) object:nil];
//start 是同步方式
[invocationOperation start];
NSLog(@"end----");
});
}
輸出結(jié)果:
- 任務(wù)在子線程中執(zhí)行(因?yàn)槭亲又骶€程中調(diào)用)
-
end最后輸出,start方式執(zhí)行任務(wù)是同步執(zhí)行,睡眠時(shí)阻塞當(dāng)前線程。
2019-01-03 16:01:59.252994+0800 Thread[5075:66813] 主線程
2019-01-03 16:01:59.254415+0800 Thread[5075:66861] invocation ==== 0
2019-01-03 16:02:00.257107+0800 Thread[5075:66861] invocation ==== 1
2019-01-03 16:02:01.259739+0800 Thread[5075:66861] invocation ==== 2
2019-01-03 16:02:02.261961+0800 Thread[5075:66861] end----
由此可見(jiàn),使用NSInvocationOperation + start方式執(zhí)行任務(wù),在哪個(gè)線程調(diào)用,就在哪個(gè)線程執(zhí)行,執(zhí)行方式是同步執(zhí)行。
2.1.2NSBlockOperation
使用NSBlockOperation創(chuàng)建一個(gè)任務(wù),并使用start方式開(kāi)始任務(wù)
- (void)clickNSOperation{
NSLog(@"主線程");
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
for (NSInteger index = 0; index < 3; index ++) {
NSLog(@"1 ==== %ld",(long)index);
[NSThread sleepForTimeInterval:1.0];
}
}];
[blockOperation start];
NSLog(@"end----");
}
輸出結(jié)果:
- 在主線程中執(zhí)行(因?yàn)槭窃谥骶€程中調(diào)用)
-
end最后輸出,start方式執(zhí)行任務(wù)是同步執(zhí)行,睡眠時(shí)阻塞當(dāng)前線程。
2019-01-03 16:41:28.661599+0800 Thread[5506:85345] 主線程
2019-01-03 16:41:28.662055+0800 Thread[5506:85345] 1 ==== 0
2019-01-03 16:41:29.663436+0800 Thread[5506:85345] 1 ==== 1
2019-01-03 16:41:30.664878+0800 Thread[5506:85345] 1 ==== 2
2019-01-03 16:41:31.666310+0800 Thread[5506:85345] end----
執(zhí)行一個(gè)任務(wù)時(shí),省略在子線程調(diào)用start執(zhí)行任務(wù),因?yàn)榻Y(jié)果和NSInvocationOperation一樣在哪個(gè)線程調(diào)用,就在哪個(gè)線程同步執(zhí)行。
NSBlockOperation還有一個(gè)方法addExecutionBlock用來(lái)添加額外任務(wù),可用于執(zhí)行多個(gè)任務(wù),比如說(shuō)想同時(shí)執(zhí)行2個(gè)任務(wù)。
- (void)clickNSOperation{
NSLog(@"主線程");
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:^{
for (NSInteger index = 0; index < 3; index ++) {
NSLog(@"1 ==== %ld",(long)index);
[NSThread sleepForTimeInterval:1.0];
}
}];
[blockOperation addExecutionBlock:^{
for (NSInteger index = 0; index < 3; index ++) {
NSLog(@"2 ==== %ld",(long)index);
[NSThread sleepForTimeInterval:1.0];
}
}];
[blockOperation start];
NSLog(@"end----");
}
輸出結(jié)果:
- 任務(wù)1在主線程執(zhí)行,任務(wù)2在子線程執(zhí)行。由此證明,
addExecutionBlock添加的任務(wù),可能在其他子線程執(zhí)行。 -
end最后輸出,start方式執(zhí)行任務(wù)是同步執(zhí)行,睡眠時(shí)阻塞當(dāng)前線程。
2019-01-03 16:47:02.219309+0800 Thread[5552:87869] 主線程
2019-01-03 16:47:02.219847+0800 Thread[5552:87896] 1 ==== 0
2019-01-03 16:47:02.219870+0800 Thread[5552:87869] 2 ==== 0
2019-01-03 16:47:03.221204+0800 Thread[5552:87896] 1 ==== 1
2019-01-03 16:47:03.221214+0800 Thread[5552:87869] 2 ==== 1
2019-01-03 16:47:04.222522+0800 Thread[5552:87896] 1 ==== 2
2019-01-03 16:47:04.222522+0800 Thread[5552:87869] 2 ==== 2
2019-01-03 16:47:05.223986+0800 Thread[5552:87869] end----
使用addExecutionBlock方式執(zhí)行多個(gè)任務(wù)時(shí),會(huì)開(kāi)啟線程,具體開(kāi)啟幾個(gè)線程是由系統(tǒng)決定的。
2.1.3 自定義類繼承NSOperation
創(chuàng)建一個(gè)類繼承自NSOperation,這里創(chuàng)建的是XXOperation
#import "XXOperation.h"
@interface XXOperation ()
@property (nonatomic , copy) NSString *operName;
@property (nonatomic , assign) BOOL over;
@end
@implementation XXOperation
- (instancetype)initWithName:(NSString *)operName{
if (self = [super init]) {
self.operName = operName;
}
return self;
}
- (void)main{
for (NSInteger index = 0; index < 3; index ++) {
NSLog(@"index ==== %d,operName = %@",index,self.operName);
[NSThread sleepForTimeInterval:1.0];
}
}
使用start方式調(diào)用
- (void)clickNSOperation{
NSLog(@"主線程");
XXOperation *operationA = [[XXOperation alloc]initWithName:@"operationA"];
[operationA start];
NSLog(@"end---");
}
輸出結(jié)果:
- 在主線程中執(zhí)行(因?yàn)槭窃谥骶€程中調(diào)用)
-
end最后輸出,start方式執(zhí)行任務(wù)是同步執(zhí)行,睡眠時(shí)阻塞當(dāng)前線程。
2019-01-03 16:20:16.400576+0800 Thread[5270:75970] 主線程
2019-01-03 16:20:16.401009+0800 Thread[5270:75970] index ==== 0,operName = operationA
2019-01-03 16:20:17.401740+0800 Thread[5270:75970] index ==== 1,operName = operationA
2019-01-03 16:20:18.402675+0800 Thread[5270:75970] index ==== 2,operName = operationA
2019-01-03 16:20:19.404857+0800 Thread[5270:75970] end---
自定義類繼承NSOperation在子線程中調(diào)用任務(wù),這里就省略了,
因?yàn)榻Y(jié)果和NSInvocationOperation、NSBlockOperation執(zhí)行一個(gè)任務(wù)時(shí)一樣,在哪個(gè)線程調(diào)用,就在哪個(gè)線程同步執(zhí)行任務(wù)。
但是我們常見(jiàn)的需求中是異步并發(fā)執(zhí)行,如何實(shí)現(xiàn)異步并發(fā)執(zhí)行呢?我們需要使用NSOperation + NSOperationQueue實(shí)現(xiàn)異步并發(fā)。
隊(duì)列又分為兩種,添加到主隊(duì)列的任務(wù),只能在主線程執(zhí)行,除addExecutionBlock添加的額外任務(wù),可能在其他線程執(zhí)行。添加到其他隊(duì)列的任務(wù)在子線程執(zhí)行。
由此可見(jiàn),使用 NSOperation + 其他隊(duì)列是較佳方法,既不阻塞主線程,又能執(zhí)行其他任務(wù)。
2.2創(chuàng)建隊(duì)列
2.2.1主隊(duì)列
//獲取主隊(duì)列
NSOperationQueue *queue = [NSOperationQueue mainQueue];
2.2.2其他隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
3.添加任務(wù)到隊(duì)列
-
addOperation:(NSOperation *)op: 直接添加一個(gè)NSOperation操作 -
addOperations:(NSArray<NSOperation *> *)ops waitUntilFinished:(BOOL)wait: 添加一組操作,wait標(biāo)志是否阻塞當(dāng)前線程直到所有操作結(jié)束 -
addOperationWithBlock:(void (^)(void))block:將block中的操作加入隊(duì)列
這里使用addOperation方式添加任務(wù)到隊(duì)列,并且不設(shè)置maxConcurrentOperationCount
- (void)clickNSOperation{
XXOperation *operationA = [[XXOperation alloc]initWithName:@"operationA"];
XXOperation *operationB = [[XXOperation alloc]initWithName:@"operationB"];
XXOperation *operationC = [[XXOperation alloc]initWithName:@"operationC"];
XXOperation *operationD = [[XXOperation alloc]initWithName:@"operationD"];
if (!self.operationQueue) {
self.operationQueue = [[NSOperationQueue alloc]init];
}
[self.operationQueue addOperation:operationA];
[self.operationQueue addOperation:operationB];
[self.operationQueue addOperation:operationC];
[self.operationQueue addOperation:operationD];
}
輸出結(jié)果:
- 在多個(gè)子線程中執(zhí)行,并發(fā)執(zhí)行;
-
end在任務(wù)之前輸出,異步執(zhí)行。
2019-01-03 17:58:51.841334+0800 Thread[6257:118424] 主線程
2019-01-03 17:58:51.841926+0800 Thread[6257:118424] end---
2019-01-03 17:58:51.841962+0800 Thread[6257:118471] index ==== 0,operName = operationC
2019-01-03 17:58:51.841962+0800 Thread[6257:118472] index ==== 0,operName = operationD
2019-01-03 17:58:51.841963+0800 Thread[6257:118473] index ==== 0,operName = operationB
2019-01-03 17:58:51.841975+0800 Thread[6257:118474] index ==== 0,operName = operationA
2019-01-03 17:58:52.847300+0800 Thread[6257:118471] index ==== 1,operName = operationC
2019-01-03 17:58:52.847303+0800 Thread[6257:118472] index ==== 1,operName = operationD
2019-01-03 17:58:52.847300+0800 Thread[6257:118473] index ==== 1,operName = operationB
2019-01-03 17:58:52.847303+0800 Thread[6257:118474] index ==== 1,operName = operationA
2019-01-03 17:58:53.848148+0800 Thread[6257:118473] index ==== 2,operName = operationB
2019-01-03 17:58:53.848148+0800 Thread[6257:118474] index ==== 2,operName = operationA
2019-01-03 17:58:53.848148+0800 Thread[6257:118472] index ==== 2,operName = operationD
2019-01-03 17:58:53.848173+0800 Thread[6257:118471] index ==== 2,operName = operationC
4.控制串行、并行
NSOperationQueue通過(guò)設(shè)置maxConcurrentOperationCount來(lái)實(shí)現(xiàn)任務(wù)是串行還是并行。
maxConcurrentOperationCount: 最大操作并發(fā)數(shù),用來(lái)控制一個(gè)隊(duì)列中有多少個(gè)任務(wù)同時(shí)進(jìn)行,而不是并發(fā)線程的數(shù)量。
在NSOperation.h里面,可以看見(jiàn) maxConcurrentOperationCount默認(rèn)值為-1
static const NSInteger NSOperationQueueDefaultMaxConcurrentOperationCount = -1;
-
maxConcurrentOperationCount默認(rèn)值為-1,不進(jìn)行限制,可以并行執(zhí)行; -
maxConcurrentOperationCount = 1為串行隊(duì)列,只能串行執(zhí)行; -
maxConcurrentOperationCount > 1為并行隊(duì)列,可以并行執(zhí)行,但是設(shè)置的值不能超過(guò)系統(tǒng)限制的最大值,如果超過(guò)系統(tǒng)限制的最大值,設(shè)置的值 = 系統(tǒng)限制的最大值;
此時(shí)將maxConcurrentOperationCount 設(shè)置為1
- (void)clickNSOperation{
NSLog(@"主線程");
XXOperation *operationA = [[XXOperation alloc]initWithName:@"operationA"];
XXOperation *operationB = [[XXOperation alloc]initWithName:@"operationB"];
XXOperation *operationC = [[XXOperation alloc]initWithName:@"operationC"];
XXOperation *operationD = [[XXOperation alloc]initWithName:@"operationD"];
if (!self.operationQueue) {
self.operationQueue = [[NSOperationQueue alloc]init];
}
self.operationQueue.maxConcurrentOperationCount = 1;//值 = 1,串行隊(duì)列,
[self.operationQueue addOperation:operationA];
[self.operationQueue addOperation:operationB];
[self.operationQueue addOperation:operationC];
[self.operationQueue addOperation:operationD];
NSLog(@"end---");
}
輸出結(jié)果:
-
end在任務(wù)之前輸出,異步執(zhí)行; - 任務(wù)在多個(gè)子線程中執(zhí)行;
- 任務(wù)一個(gè)接著一個(gè)串行執(zhí)行,最后呈現(xiàn)結(jié)果就是異步串行。
由此證明:maxConcurrentOperationCount,用來(lái)控制一個(gè)隊(duì)列中有多少個(gè)任務(wù)同時(shí)進(jìn)行,而不是并發(fā)線程的數(shù)量。
2019-01-03 18:14:37.042902+0800 Thread[6370:123969] 主線程
2019-01-03 18:14:37.043486+0800 Thread[6370:123969] end---
2019-01-03 18:14:37.043535+0800 Thread[6370:124020] index ==== 0,operName = operationA
2019-01-03 18:14:38.043839+0800 Thread[6370:124020] index ==== 1,operName = operationA
2019-01-03 18:14:39.049215+0800 Thread[6370:124020] index ==== 2,operName = operationA
2019-01-03 18:14:40.054252+0800 Thread[6370:124021] index ==== 0,operName = operationB
2019-01-03 18:14:41.059064+0800 Thread[6370:124021] index ==== 1,operName = operationB
2019-01-03 18:14:42.061843+0800 Thread[6370:124021] index ==== 2,operName = operationB
2019-01-03 18:14:43.067389+0800 Thread[6370:124021] index ==== 0,operName = operationC
2019-01-03 18:14:44.072883+0800 Thread[6370:124021] index ==== 1,operName = operationC
2019-01-03 18:14:45.074068+0800 Thread[6370:124021] index ==== 2,operName = operationC
2019-01-03 18:14:46.076217+0800 Thread[6370:124020] index ==== 0,operName = operationD
2019-01-03 18:14:47.079396+0800 Thread[6370:124020] index ==== 1,operName = operationD
2019-01-03 18:14:48.084846+0800 Thread[6370:124020] index ==== 2,operName = operationD
將最大并發(fā)數(shù)設(shè)置為4
self.operationQueue.maxConcurrentOperationCount = 4;//值 = 4,并行隊(duì)列
輸出結(jié)果:
-
end在任務(wù)之前輸出,異步執(zhí)行; - 任務(wù)在多個(gè)子線程中執(zhí)行;
- 任務(wù)并發(fā)執(zhí)行,最后呈現(xiàn)結(jié)果就是實(shí)現(xiàn)異步并發(fā)執(zhí)行(最完美狀態(tài))
2019-01-03 18:19:58.120798+0800 Thread[6424:126327] 主線程
2019-01-03 18:19:58.121325+0800 Thread[6424:126327] end---
2019-01-03 18:19:58.121362+0800 Thread[6424:126374] index ==== 0,operName = operationB
2019-01-03 18:19:58.121339+0800 Thread[6424:126371] index ==== 0,operName = operationA
2019-01-03 18:19:58.121362+0800 Thread[6424:126373] index ==== 0,operName = operationD
2019-01-03 18:19:58.121371+0800 Thread[6424:126372] index ==== 0,operName = operationC
2019-01-03 18:19:59.122232+0800 Thread[6424:126372] index ==== 1,operName = operationC
2019-01-03 18:19:59.122231+0800 Thread[6424:126371] index ==== 1,operName = operationA
2019-01-03 18:19:59.122287+0800 Thread[6424:126373] index ==== 1,operName = operationD
2019-01-03 18:19:59.122231+0800 Thread[6424:126374] index ==== 1,operName = operationB
2019-01-03 18:20:00.127789+0800 Thread[6424:126371] index ==== 2,operName = operationA
2019-01-03 18:20:00.127789+0800 Thread[6424:126374] index ==== 2,operName = operationB
2019-01-03 18:20:00.127791+0800 Thread[6424:126372] index ==== 2,operName = operationC
2019-01-03 18:20:00.127791+0800 Thread[6424:126373] index ==== 2,operName = operationD
5. 操作依賴
操作依賴 : 控制任務(wù)是否進(jìn)入就緒狀態(tài),從而控制任務(wù)執(zhí)行順序
-
- (void)addDependency:(NSOperation *)op;:添加依賴 -
(void)removeDependency:(NSOperation *)op: 刪除依賴 -
@property (readonly, copy) NSArray<NSOperation *> *dependencies:返回當(dāng)前依賴數(shù)組
上一個(gè)例子中,有4個(gè)任務(wù),分別是A、B、C、D ,設(shè)置D依賴于A ,A 依賴于C,C 依賴于B,千萬(wàn)不能再設(shè)置B依賴于D,不然就是死循環(huán)了。
- (void)clickNSOperation{
NSLog(@"主線程");
XXOperation *operationA = [[XXOperation alloc]initWithName:@"operationA"];
XXOperation *operationB = [[XXOperation alloc]initWithName:@"operationB"];
XXOperation *operationC = [[XXOperation alloc]initWithName:@"operationC"];
XXOperation *operationD = [[XXOperation alloc]initWithName:@"operationD"];
if (!self.operationQueue) {
self.operationQueue = [[NSOperationQueue alloc]init];
}
// self.operationQueue.maxConcurrentOperationCount = 1;//值 = 1,串行隊(duì)列
self.operationQueue.maxConcurrentOperationCount = 4;//值 = 4,并行隊(duì)列
// 設(shè)置D依賴于A ,A 依賴于C,C 依賴于B
[operationD addDependency:operationA];
[operationA addDependency:operationC];
[operationC addDependency:operationB];
[self.operationQueue addOperation:operationA];
[self.operationQueue addOperation:operationB];
[self.operationQueue addOperation:operationC];
[self.operationQueue addOperation:operationD];
NSLog(@"end---");
}
輸出結(jié)果:
-
end在任務(wù)之前輸出,異步執(zhí)行; - 任務(wù)執(zhí)行順序B、C、A、D。
本來(lái)添加到隊(duì)列的任務(wù)順序是A、B、C、D,執(zhí)行A時(shí),A依賴于C,C還未執(zhí)行結(jié)束,A處于未就緒狀態(tài),只有等C執(zhí)行結(jié)束后,A才能處于就緒狀態(tài),才能執(zhí)行任務(wù)。B沒(méi)有依賴,直接執(zhí)行,同理C依賴于B,,D依賴于A,所以執(zhí)行順序是B、C、A、D
2019-01-04 16:36:29.830947+0800 Thread[19747:188253] 主線程
2019-01-04 16:36:29.831568+0800 Thread[19747:188253] end---
2019-01-04 16:36:29.831603+0800 Thread[19747:188290] index ==== 0,operName = operationB
2019-01-04 16:36:30.832856+0800 Thread[19747:188290] index ==== 1,operName = operationB
2019-01-04 16:36:31.837187+0800 Thread[19747:188290] index ==== 2,operName = operationB
2019-01-04 16:36:32.842000+0800 Thread[19747:188289] index ==== 0,operName = operationC
2019-01-04 16:36:33.846089+0800 Thread[19747:188289] index ==== 1,operName = operationC
2019-01-04 16:36:34.851604+0800 Thread[19747:188289] index ==== 2,operName = operationC
2019-01-04 16:36:35.856626+0800 Thread[19747:188290] index ==== 0,operName = operationA
2019-01-04 16:36:36.859237+0800 Thread[19747:188290] index ==== 1,operName = operationA
2019-01-04 16:36:37.862654+0800 Thread[19747:188290] index ==== 2,operName = operationA
2019-01-04 16:36:38.866634+0800 Thread[19747:188289] index ==== 0,operName = operationD
2019-01-04 16:36:39.870859+0800 Thread[19747:188289] index ==== 1,operName = operationD
2019-01-04 16:36:40.873716+0800 Thread[19747:188289] index ==== 2,operName = operationD
6.優(yōu)先級(jí)
queuePriority,設(shè)置隊(duì)列中的任務(wù)的優(yōu)先級(jí),目的是:控制進(jìn)入就緒狀態(tài)的任務(wù)的執(zhí)行順序。
沒(méi)有設(shè)置優(yōu)先級(jí)默認(rèn)是NSOperationQueuePriorityNormal,幾個(gè)優(yōu)先級(jí)有以下幾個(gè)選項(xiàng):
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
};
什么樣的任務(wù)處于就緒狀態(tài)呢?
添加到隊(duì)列中,并且依賴關(guān)系都已經(jīng)滿足,比如上面A依賴于C,C還未執(zhí)行結(jié)束,A就處于未就緒狀態(tài),C執(zhí)行結(jié)束后,A才處于就緒狀態(tài)。
以下幾種條件,都是在maxConcurrentOperationCount = 1的情況下,剔除并發(fā)引起的問(wèn)題。
如果隊(duì)列中的任務(wù)都處于就緒狀態(tài)下,并且都沒(méi)有設(shè)置優(yōu)先級(jí),都是默認(rèn)優(yōu)先級(jí)的時(shí)候,任務(wù)的執(zhí)行順序,就是先添加到隊(duì)列里面的任務(wù)先執(zhí)行。(控制串行、并行的第一個(gè)例子可以證明)
如果隊(duì)列中的任務(wù)都處于就緒狀態(tài)下,并且設(shè)置優(yōu)先級(jí)的時(shí)候,任務(wù)的執(zhí)行順序,就是優(yōu)先級(jí)越高的任務(wù)先執(zhí)行。
如果隊(duì)列中的任務(wù)有處于就緒狀態(tài)的,有未處于就緒狀態(tài)的,就緒狀態(tài)的優(yōu)先級(jí)低,未就緒狀態(tài)的優(yōu)先級(jí)高,此時(shí)還是執(zhí)行就緒狀態(tài)優(yōu)先級(jí)低的操作,因?yàn)橐蕾囮P(guān)系控制任務(wù)是否進(jìn)入就緒狀態(tài),從而控制任務(wù)執(zhí)行順序,而優(yōu)先級(jí)則是控制進(jìn)入就緒狀態(tài)任務(wù)的執(zhí)行順序。
情況2的實(shí)現(xiàn)代碼
- (void)clickNSOperation{
NSLog(@"主線程");
XXOperation *operationA = [[XXOperation alloc]initWithName:@"operationA"];
XXOperation *operationB = [[XXOperation alloc]initWithName:@"operationB"];
XXOperation *operationC = [[XXOperation alloc]initWithName:@"operationC"];
XXOperation *operationD = [[XXOperation alloc]initWithName:@"operationD"];
if (!self.operationQueue) {
self.operationQueue = [[NSOperationQueue alloc]init];
}
self.operationQueue.maxConcurrentOperationCount = 1;//值 = 1,串行隊(duì)列
// self.operationQueue.maxConcurrentOperationCount = 4;//值 = 4,并行隊(duì)列
//設(shè)置優(yōu)先級(jí)
operationA.queuePriority = NSOperationQueuePriorityVeryHigh;
operationB.queuePriority = NSOperationQueuePriorityVeryHigh;
operationC.queuePriority = NSOperationQueuePriorityHigh;
operationD.queuePriority = NSOperationQueuePriorityVeryHigh;
[self.operationQueue addOperation:operationA];
[self.operationQueue addOperation:operationB];
[self.operationQueue addOperation:operationC];
[self.operationQueue addOperation:operationD];
NSLog(@"end---");
輸出結(jié)果:任務(wù)執(zhí)行順序A、B、D、C,跟任務(wù)優(yōu)先級(jí)關(guān)系成正比,優(yōu)先級(jí)越高,越先執(zhí)行,C優(yōu)先級(jí)比D低,所以D先執(zhí)行。
2019-01-07 10:58:02.635752+0800 Thread[1130:25079] index ==== 0,operName = operationA
2019-01-07 10:58:03.641122+0800 Thread[1130:25079] index ==== 1,operName = operationA
2019-01-07 10:58:04.645798+0800 Thread[1130:25079] index ==== 2,operName = operationA
2019-01-07 10:58:05.651342+0800 Thread[1130:25076] index ==== 0,operName = operationB
2019-01-07 10:58:06.651660+0800 Thread[1130:25076] index ==== 1,operName = operationB
2019-01-07 10:58:07.656731+0800 Thread[1130:25076] index ==== 2,operName = operationB
2019-01-07 10:58:08.661404+0800 Thread[1130:25079] index ==== 0,operName = operationD
2019-01-07 10:58:09.666161+0800 Thread[1130:25079] index ==== 1,operName = operationD
2019-01-07 10:58:10.668855+0800 Thread[1130:25079] index ==== 2,operName = operationD
2019-01-07 10:58:11.672342+0800 Thread[1130:25076] index ==== 0,operName = operationC
2019-01-07 10:58:12.676247+0800 Thread[1130:25076] index ==== 1,operName = operationC
2019-01-07 10:58:13.677245+0800 Thread[1130:25076] index ==== 2,operName = operationC
情況3的實(shí)現(xiàn)代碼
- (void)clickNSOperation{
NSLog(@"主線程");
XXOperation *operationA = [[XXOperation alloc]initWithName:@"operationA"];
XXOperation *operationB = [[XXOperation alloc]initWithName:@"operationB"];
XXOperation *operationC = [[XXOperation alloc]initWithName:@"operationC"];
XXOperation *operationD = [[XXOperation alloc]initWithName:@"operationD"];
if (!self.operationQueue) {
self.operationQueue = [[NSOperationQueue alloc]init];
}
self.operationQueue.maxConcurrentOperationCount = 1;//值 = 1,串行隊(duì)列
// self.operationQueue.maxConcurrentOperationCount = 4;//值 = 4,并行隊(duì)列
// 設(shè)置D依賴于A ,A 依賴于C,C 依賴于B
[operationD addDependency:operationA];
[operationA addDependency:operationC];
[operationC addDependency:operationB];
//設(shè)置優(yōu)先級(jí)
operationA.queuePriority = NSOperationQueuePriorityVeryHigh;
operationB.queuePriority = NSOperationQueuePriorityVeryHigh;
operationC.queuePriority = NSOperationQueuePriorityHigh;
operationD.queuePriority = NSOperationQueuePriorityVeryHigh;
[self.operationQueue addOperation:operationA];
[self.operationQueue addOperation:operationB];
[self.operationQueue addOperation:operationC];
[self.operationQueue addOperation:operationD];
NSLog(@"end---");
輸出結(jié)果:
如果只設(shè)置依賴關(guān)系,不設(shè)置優(yōu)先級(jí),執(zhí)行順序是B、C、A、D
如果不設(shè)置依賴關(guān)系,只設(shè)置優(yōu)先級(jí),執(zhí)行順序是A、B、D、C
同時(shí)設(shè)置依賴關(guān)系和優(yōu)先級(jí),執(zhí)行順序依舊是B、C、A、D,因?yàn)橐蕾囮P(guān)系控制任務(wù)是否進(jìn)入就緒狀態(tài),從而控制任務(wù)執(zhí)行順序,而優(yōu)先級(jí)則是控制進(jìn)入就緒狀態(tài)任務(wù)的執(zhí)行順序。因?yàn)镈依賴于A ,A 依賴于C,C 依賴于B,所以此時(shí)就緒狀態(tài)決定執(zhí)行順序。
2019-01-07 11:18:25.281981+0800 Thread[1162:31573] 主線程
2019-01-07 11:18:25.282591+0800 Thread[1162:31573] end---
2019-01-07 11:18:25.282638+0800 Thread[1162:31615] index ==== 0,operName = operationB
2019-01-07 11:18:26.287156+0800 Thread[1162:31615] index ==== 1,operName = operationB
2019-01-07 11:18:27.292264+0800 Thread[1162:31615] index ==== 2,operName = operationB
2019-01-07 11:18:28.297512+0800 Thread[1162:31615] index ==== 0,operName = operationC
2019-01-07 11:18:29.301186+0800 Thread[1162:31615] index ==== 1,operName = operationC
2019-01-07 11:18:30.302530+0800 Thread[1162:31615] index ==== 2,operName = operationC
2019-01-07 11:18:31.304189+0800 Thread[1162:31615] index ==== 0,operName = operationA
2019-01-07 11:18:32.304707+0800 Thread[1162:31615] index ==== 1,operName = operationA
2019-01-07 11:18:33.308931+0800 Thread[1162:31615] index ==== 2,operName = operationA
2019-01-07 11:18:34.309684+0800 Thread[1162:31615] index ==== 0,operName = operationD
2019-01-07 11:18:35.313671+0800 Thread[1162:31615] index ==== 1,operName = operationD
2019-01-07 11:18:36.317910+0800 Thread[1162:31615] index ==== 2,operName = operationD
7.線程間的通信
在iOS開(kāi)發(fā)工程中,我們一般在主線程中進(jìn)行UI刷新,如:點(diǎn)擊、拖拽、滾動(dòng)事件,耗時(shí)操作放在其他線程中,而當(dāng)耗時(shí)操作結(jié)束后,回到主線程,就需要用到線程間的通信
- (void)clickNSOperation{
NSLog(@"主線程");
self.operationQueue = [[NSOperationQueue alloc]init];
[self.operationQueue addOperationWithBlock:^{
for (NSInteger index = 0; index < 3; index ++) {
NSLog(@"invocation ==== %ld",(long)index);
[NSThread sleepForTimeInterval:1.0];
}
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"回到主線程");
NSLog(@"end---");
}];
}];
}
輸出結(jié)果:完成耗時(shí)操作后,回到了主線程
2019-01-07 13:59:09.784407+0800 Thread[1449:46686] 主線程
2019-01-07 13:59:09.785071+0800 Thread[1449:46730] invocation ==== 0
2019-01-07 13:59:10.789103+0800 Thread[1449:46730] invocation ==== 1
2019-01-07 13:59:11.793907+0800 Thread[1449:46730] invocation ==== 2
2019-01-07 13:59:12.796989+0800 Thread[1449:46686] 回到主線程
2019-01-07 13:59:12.797208+0800 Thread[1449:46686] end---
8.線程安全與線程同步
線程安全:在多個(gè)線程中同時(shí)訪問(wèn)并操作同一對(duì)象,運(yùn)行結(jié)果與預(yù)期的值相同就是線程安全。線程安全問(wèn)題都是由全局變量及靜態(tài)變量引起的,若每個(gè)線程中對(duì)全局變量、靜態(tài)變量只有讀操作,而無(wú)寫操作,一般來(lái)說(shuō),這個(gè)全局變量是線程安全的;若有多個(gè)線程同時(shí)執(zhí)行寫操作,一般都需要考慮線程同步,否則的話就可能影響線程安全。
線程同步:可理解為線程A和B一塊配合,A執(zhí)行到一定程度時(shí)要依靠B的某個(gè)結(jié)果,于是停下來(lái),示意B運(yùn)行;B依言執(zhí)行,再將結(jié)果給A;A再繼續(xù)操作。
例子還是用之前的賣票例子,兩個(gè)站點(diǎn)賣票,創(chuàng)建一個(gè)TicketManager,在外面調(diào)用startToSale方法,TicketManager里面sale方法加鎖,保證線程安全。
// TicketManager.m
// Thread
//
// Created by Summer on 2018/12/25.
// Copyright ? 2018 Summer. All rights reserved.
//
#import "TicketManager.h"
#define Total 50
@interface TicketManager ()
@property (nonatomic,assign) NSInteger tickets;//剩余票數(shù)
@property (nonatomic,assign) NSInteger saleCount;//賣出票數(shù)
//使用NSThread
//@property (nonatomic,strong) NSThread *threadBJ;//北京票點(diǎn)
//@property (nonatomic,strong) NSThread *threadSH;//上海票點(diǎn)
//使用NSInvocationOperation + NSOperationQueue
@property (nonatomic , strong) NSInvocationOperation *operationBJ;
@property (nonatomic , strong) NSInvocationOperation *operationSH;
@property (nonatomic , strong) NSOperationQueue *queue;
/*
NSLock、NSConditionLock、NSRecursiveLock、NSCondition加鎖方式都一樣,都是實(shí)現(xiàn)NSLocking協(xié)議
*/
@property (nonatomic,strong) NSCondition *condition;
@property (nonatomic , strong) dispatch_semaphore_t semaphore;
@end
@implementation TicketManager
- (instancetype)init{
self = [super init];
if (self) {
_condition = [[NSCondition alloc]init];
_semaphore = dispatch_semaphore_create(1);
_tickets = Total;
// _threadBJ = [[NSThread alloc]initWithTarget:self selector:@selector(sale) object:nil];
// _threadSH = [[NSThread alloc]initWithTarget:self selector:@selector(sale) object:nil];
// [_threadBJ setName:@"北京"];
// [_threadSH setName:@"上海"];
_operationBJ = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(sale) object:nil];
[_operationBJ setName:@"北京"];
_operationSH = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(sale) object:nil];
[_operationSH setName:@"上海"];
}
return self;
}
- (void)sale{
while (1) {
//1、synchronized
@synchronized (self) {
if (self.tickets > 0 ) {
[NSThread sleepForTimeInterval:0.1];
self.tickets --;
self.saleCount = Total - self.tickets;
NSLog(@"%@ , 賣出 = %ld,剩余= %ld",[NSThread currentThread],(long)self.saleCount,(long)self.tickets);
}else{
break;//一定要break,不然就會(huì)死循環(huán)
}
}
// 2、NSCondition
// [self.condition lock];
// if (self.tickets > 0 ) {
// [NSThread sleepForTimeInterval:0.1];
// self.tickets --;
// self.saleCount = Total - self.tickets;
// NSLog(@"%@ , 賣出 = %d,剩余= %d",[NSThread currentThread].name,self.saleCount,self.tickets);
// }else{
// break;
// }
// [self.condition unlock];
//
//3、dispatch_semaphore方式
// dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
// if (self.tickets > 0 ) {
// [NSThread sleepForTimeInterval:0.1];
// self.tickets --;
// self.saleCount = Total - self.tickets;
// NSLog(@"%@ , 賣出 = %d,剩余= %d",[NSThread currentThread].name,self.saleCount,self.tickets);
// }else{
// dispatch_semaphore_signal(self.semaphore);
// break;
// }
// dispatch_semaphore_signal(self.semaphore);
}
}
- (void)startToSale{
// [_threadSH start];
// [_threadBJ start];
_queue = [[NSOperationQueue alloc]init];
[_queue addOperation:_operationBJ];
[_queue addOperation:_operationSH];
}
輸出結(jié)果:正常賣票
省略一部分
2019-01-07 14:46:23.488836+0800 Thread[1622:63417] , 賣出 = 45,剩余= 5
2019-01-07 14:46:23.592623+0800 Thread[1622:63417] , 賣出 = 46,剩余= 4
2019-01-07 14:46:23.695578+0800 Thread[1622:63417] , 賣出 = 47,剩余= 3
2019-01-07 14:46:23.798669+0800 Thread[1622:63417] , 賣出 = 48,剩余= 2
2019-01-07 14:46:23.903085+0800 Thread[1622:63417] , 賣出 = 49,剩余= 1
2019-01-07 14:46:24.007916+0800 Thread[1622:63417] , 賣出 = 50,剩余= 0
到此NSOperation + NSOperationQueue就結(jié)束了,多線程系列也結(jié)束了,2019繼續(xù)加油~~
參考博客:
iOS 多線程:『NSOperation、NSOperationQueue』詳盡總結(jié)
iOS多線程慕課網(wǎng)視頻
文章鏈接:
iOS 多線程- pThread和NSThread
iOS 多線程-GCD
喜歡就點(diǎn)個(gè)贊吧????
有錯(cuò)之處,還請(qǐng)指出,感謝????