iOS使用线程的方式
- pthread
- NSThread
- GCD
- NSOperation
NSThread线程的创建与启动
- 创建线程后启动线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[thread start];
- 创建线程后自动启动线程
NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
- 隐式创建线程
[self performSelectorInBackground:@selector(run) withObject:nil];
NSThread线程相关方法
- 获取当前线程
[NSThread currentThread];
//获取主线程
[NSThread mainThread];
- 是否是主线程
[NSThread isMainThread];
- 设置线程名称
[[NSThread currentThread] setName:@"设置线程名称"];
- 启动线程
[[NSThread currentThread] start];
- 阻塞线程 让线程休息会
//[NSThread sleepUntilDate:[]]
[NSThread sleepForTimeInterval:2];
NSThread线程间通信
- 子线程->主线程 子线程->指定线程
self performSelectorOnMainThread:<#(nonnull SEL)#> withObject:<#(nullable id)#> waitUntilDone:<#(BOOL)#>
self performSelector:<#(nonnull SEL)#> onThread:<#(nonnull NSThread *)#> withObject:<#(nullable id)#> waitUntilDone:<#(BOOL)#>
- 举例说明(项目中经典例子 图片下载及 图片UI显示)
[self performSelectorInBackground:@selector(downloadImage) withObject:nil];
//在子线程下载图片
- (void)downloadImage{
NSURL *imageUrl = [NSURL URLWithString:@"图片地址"];
NSData *imageData = [NSData dataWithContentsOfURL:imageUrl];
UIImage *image = [UIImage imageWithData:imageData];
//在主线程将图片显示
[self performSelectorOnMainThread:@selector(refreshOnMainThread:) withObject:image waitUntilDone:true];
}
- (void)refreshOnMainThread:(UIImage *) image {
UIImageView *imageV = [[UIImageView alloc] initWithImage:image];
}
NSThread线程安全
- @synchronized (self)
性能是较差的,但是很方便,不用自己创建锁,不用加锁,一般项目都能满足
- (void)myMethod:(id)anObj
{
@synchronized(anObj)
{
// Everything between the braces is protected by the @synchronized directive.
}
}
- NSLock
在Cocoa程序中NSLock中实现了一个简单的互斥锁。所有锁(包括NSLock)的接口实际上都是通过NSLocking协议定义的,它定义了lock和unlock方法。你使用这些方法来获取和释放该锁
- (NSLock *)lock
{
if (!_lock)
{
_lock = [[NSLock alloc] init];
}
return _lock;
}
[self.lock lock];
self->_myVTKPolyData->SetPoints(m_Points);
self->_myVTKPolyData->SetLines(lines);
self->_myVTKPolyData->GetPointData()->SetScalars(colors);
[self.lock unlock];
-dispatch_semaphore 利用信号量控制(dispatch_semaphore_t sema = dispatch_semaphore_create(1);)
- (void)initTicketStatusSafe{
semaphoreLock = dispatch_semaphore_create(1); //一次只有一个
self.ticketSurplusCount = 50;
//代表售卖窗口1
dispatch_queue_t queue1 = dispatch_queue_create("peng1", DISPATCH_QUEUE_SERIAL);
//代表售卖窗口2
dispatch_queue_t queue2 = dispatch_queue_create("peng2", DISPATCH_QUEUE_SERIAL);
__weak typeof(self) weakSelf = self;
dispatch_async(queue1, ^{
[weakSelf saleTicketSafe];
});
dispatch_async(queue2, ^{
[weakSelf saleTicketSafe];
});
}
- (void)saleTicketSafe {
while (1) {
//如果还有票,继续售卖
// @synchronized (self) {
dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);
if (self.ticketSurplusCount > 0) {
self.ticketSurplusCount --;
NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%ld 窗口:%@", self.ticketSurplusCount, [NSThread currentThread].name]);
[NSThread sleepForTimeInterval:0.5];
}
//如果已卖完,关闭售票窗口
else {
NSLog(@"所有火车票均已售完");
dispatch_semaphore_signal(semaphoreLock);
break;
}
// }
dispatch_semaphore_signal(semaphoreLock);
}
}
既然讲到了GCD信号量,就直接切入GCD多线程,GCD还是非常实用
GCD
- GCD优点
1、GCD 会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
2、GCD会自动利用CPU内核(特别现在都是多核CPU,可以发挥淋漓尽致) - GCD 任务和队列
- 任务
任务具体干些什么事,执行任务有两种方式:同步执行(sync)和异步执行(async)
- 同步执行(sync)
同步执行只能在当前线程中执行任务,不具备开启线程的能力,同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行 - 异步执行(async)
异步执行具备开启新线程的能力,可以在新线程中执行任务,异步执行添加到队列中,它不需要等待,可以继续执行任务。
- 队列 在GCD中有两个队列 串型队列和并发队列,既然是队列就都符合FIFO原则,两者的区别是:执行的顺序不同和开启的线程不同
-
串型队列(Serial Dispatch Queue)
每次只有一个任务执行。所有任务在同一个线程中执行(如果是同步不会开启线程,如果是异步有且只能开启一个新线程,队列里面同时有异步和同步例外,有异步就会有且只能再开一个线程),满足FIFO原则,执行一个任务完毕才执行下一个任务; -
并行队列(Concurrent Dispatch Queue)
可以让多个任务同时执行(可以开启多个线程)并发队列的并发功能只有在异步async任务下才有用;
举个栗子,你要打电话给女朋友A 和女朋友B。
同步执行就是,你打电话给女朋友A的时候,就不能同时打电话给女朋友B,等到给女朋友A打完电话,才可以打电话给女朋友B。而且只能用当前的电话(不具备开启新线程的能力)
而异步执行就是,你打电话给女朋友A的时候,不等和女朋友A通话结束,还能直接给女朋友B打电话,不用等和女朋友A通话结束之后再打(不用等待任务执行结束)。除了当前电话,你还可以使用其他电话(具备开启新线程的能力);
- 任务
队列(Dispatch Queue):这里的队列指执行任务的等待队列,即用来存放任务的队列。队列是一种特殊的线性表。采用FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。
GCD的使用步骤
GCD使用步骤只有两步
1、创建一个队列(串型队列或者并发队列)
备注:主队列是一个特殊的串队列
//串型队列
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);
//并型队列
dispatch_queue_t queueC = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
2、将任务追加到任务的队列 中去,然后系统就会根据任务类型执行任务(同步执行或者异步执行)
dispatch_async(queue, ^{
// 异步追加任务1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(queue, ^{
// 异步追加任务2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_sync(queue, ^{
// 同步追加任务3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
});
组合

注意,在主队列中 异步是不会开启新线程执行
dispatch_queue_t queue = dispatch_get_main_queue();
//并型队列
//dispatch_queue_t queueC = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
// 异步追加任务1
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(queue, ^{
// 异步追加任务2
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
});
dispatch_async(queue, ^{
// 异步追加任务3
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印当前线程
}
});
NSLog(@"syncConcurrent---end");
//打印
currentThread---<NSThread: 0x600003b2a940>{number = 1, name = main}
syncConcurrent---begin
syncConcurrent---end
1---<NSThread: 0x600003b2a940>{number = 1, name = main}
1---<NSThread: 0x600003b2a940>{number = 1, name = main}
2---<NSThread: 0x600003b2a940>{number = 1, name = main}
2---<NSThread: 0x600003b2a940>{number = 1, name = main}
3---<NSThread: 0x600003b2a940>{number = 1, name = main}
3---<NSThread: 0x600003b2a940>{number = 1, name = main}
GCD 线程间的通信
- (void)GCDCommunication{
//获取全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//获取主队列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(queue, ^{
//异步追加任务
for (int i = 0; i < 2; i++){
[NSThread sleepForTimeInterval:2];
//打印当前线程
NSLog(@"1-----%@",[NSThread currentThread]);
}
//回到主线程
dispatch_async(mainQueue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"2----%@",[NSThread currentThread]);
});
});
}
//打印
1-----<NSThread: 0x6000021bea80>{number = 3, name = (null)}
1-----<NSThread: 0x6000021bea80>{number = 3, name = (null)}
2----<NSThread: 0x6000021b1400>{number = 1, name = main}
GCD的其它方法
GCD栅栏方法: dispatch_barrier_async
使用场景:有时我们需要异步执行两组任务,而且第一组的任务执行完之后,才能开始执行第二组任务,这样我们就需要一个一个相当于“栅栏”一样的一个方法将两组异步执行任务分割气啦,这个时候需要用到dispatch_barrier_async方法在两个任务组将形成栅栏
- (void)barrier{
dispatch_queue_t queue = dispatch_queue_create("peng", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
//异步追加任务1
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1----%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
//异步追加任务2
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"2----%@",[NSThread currentThread]);
}
});
//dispatch_barrier_sync 也可以 只是会切换到调用线程 主线程调用就是 main
dispatch_barrier_async(queue, ^{
//追加任务barrier
for (int i = 0; i < 2; i ++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"barrier----%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
//异步追加任务3
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"3----%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
//异步追加任务4
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"4----%@",[NSThread currentThread]);
}
});
}
2019-07-17 15:03:33.907484+0800 YBStockChartView[1988:60887] 1----<NSThread: 0x600001476380>{number = 4, name = (null)}
2019-07-17 15:03:33.907484+0800 YBStockChartView[1988:60889] 2----<NSThread: 0x600001400b80>{number = 3, name = (null)}
2019-07-17 15:03:35.910472+0800 YBStockChartView[1988:60887] 1----<NSThread: 0x600001476380>{number = 4, name = (null)}
2019-07-17 15:03:35.910479+0800 YBStockChartView[1988:60889] 2----<NSThread: 0x600001400b80>{number = 3, name = (null)}
2019-07-17 15:03:37.914415+0800 YBStockChartView[1988:60889] barrier----<NSThread: 0x600001400b80>{number = 3, name = (null)}
2019-07-17 15:03:39.915950+0800 YBStockChartView[1988:60889] barrier----<NSThread: 0x600001400b80>{number = 3, name = (null)}
2019-07-17 15:03:41.920864+0800 YBStockChartView[1988:60889] 3----<NSThread: 0x600001400b80>{number = 3, name = (null)}
2019-07-17 15:03:41.920869+0800 YBStockChartView[1988:60887] 4----<NSThread: 0x600001476380>{number = 4, name = (null)}
2019-07-17 15:03:43.923980+0800 YBStockChartView[1988:60889] 3----<NSThread: 0x600001400b80>{number = 3, name = (null)}
2019-07-17 15:03:43.924004+0800 YBStockChartView[1988:60887] 4----<NSThread: 0x600001476380>{number = 4, name = (null)}
GCD延时操作
- (void)after{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//2秒后异步追加到主队列执行
NSLog(@"after-----%@",[NSThread currentThread]);
});
}
GCD 只有执行一次性:dispatch_once
在整个程序运行过程中只执行一次
- (void)once {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//只执行一次
NSLog(@"只执行一次");
});
}
GCD快速迭代方法:dispatch_apply
与FOR 循环区别,for 循环 顺序遍历 ,而 dispatch_apply 不是
- (void)dispatch_apply{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSLog(@"apply-- begin");
dispatch_apply(6, queue, ^(size_t index) {
NSLog(@"%zd----%@",index,[NSThread currentThread]);
});
NSLog(@"apply---end");
}
2019-07-17 15:25:12.435116+0800 YBStockChartView[2352:77865] apply-- begin
2019-07-17 15:25:12.435942+0800 YBStockChartView[2352:77901] 4----<NSThread: 0x6000017a8980>{number = 5, name = (null)}
2019-07-17 15:25:12.435946+0800 YBStockChartView[2352:77900] 2----<NSThread: 0x6000017bfe80>{number = 4, name = (null)}
2019-07-17 15:25:12.435948+0800 YBStockChartView[2352:77904] 1----<NSThread: 0x6000017c9b00>{number = 3, name = (null)}
2019-07-17 15:25:12.435948+0800 YBStockChartView[2352:77865] 0----<NSThread: 0x6000017f1400>{number = 1, name = main}
2019-07-17 15:25:12.435955+0800 YBStockChartView[2352:77903] 3----<NSThread: 0x6000017c9a40>{number = 6, name = (null)}
2019-07-17 15:25:12.435965+0800 YBStockChartView[2352:77902] 5----<NSThread: 0x6000017a0b40>{number = 7, name = (null)}
2019-07-17 15:25:12.436115+0800 YBStockChartView[2352:77865] apply---end
GCD 队列组 dispatch_group
- (void)dispatch_group{
dispatch_group_t gruop = dispatch_group_create();
dispatch_group_async(gruop, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//追加耗时任务
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1====%@",[NSThread currentThread]);
}
});
dispatch_group_async(gruop, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//追加耗时任务
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"2====%@",[NSThread currentThread]);
}
});
dispatch_group_notify(gruop, dispatch_get_main_queue(), ^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"3====%@",[NSThread currentThread]);
}
NSLog(@"group--end");
});
}
2019-07-17 15:39:32.459319+0800 YBStockChartView[2552:87029] 2====<NSThread: 0x600002ee4780>{number = 3, name = (null)}
2019-07-17 15:39:32.459320+0800 YBStockChartView[2552:87028] 1====<NSThread: 0x600002ed8200>{number = 4, name = (null)}
2019-07-17 15:39:34.464453+0800 YBStockChartView[2552:87028] 1====<NSThread: 0x600002ed8200>{number = 4, name = (null)}
2019-07-17 15:39:34.464480+0800 YBStockChartView[2552:87029] 2====<NSThread: 0x600002ee4780>{number = 3, name = (null)}
2019-07-17 15:39:36.465933+0800 YBStockChartView[2552:86994] 3====<NSThread: 0x600002eba940>{number = 1, name = main}
2019-07-17 15:39:38.466517+0800 YBStockChartView[2552:86994] 3====<NSThread: 0x600002eba940>{number = 1, name = main}
2019-07-17 15:39:38.466685+0800 YBStockChartView[2552:86994] group--end
GCD dispatch_group_wait
暂停当前线程(阻塞),等待指定的group中的任务执行完成后,才会往下执行
- (void)groupWait{
NSLog(@"currentThread====%@",[NSThread currentThread]);
NSLog(@"group--begin");
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//追加
for (int i = 0; i < 2; i ++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1=====%@",[NSThread currentThread]);
}
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//追加
for (int i = 0; i < 2; i ++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"2=====%@",[NSThread currentThread]);
}
});
// 等待上面的任务全部完成后,会往下继续执行(会阻塞当前线程)
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"group---end");
}
GCD dispatch_group_enter、dispatch_group_leave
- dispatch_group_enter 意思是一个任务追加到group 执行一次,相当于group中的未执行完毕任务书+1
- dispatch_group_leave 意思是一个任务执行完毕离开group ,执行一次,相当于group中的未执行完毕任务书-1
- 当group中未执行完毕任务数未0的时候,才会使dispatch_group_wait解除阻塞,以及执行追加到dispatch_group_notify的任务中去
- (void)gruopEnterAndLeave{
NSLog(@"currentThread===%@",[NSThread currentThread]);
NSLog(@"group--begin");
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_enter(group);
dispatch_async(queue, ^{
for (int i = 0; i < 2; i ++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1==-%@",[NSThread currentThread]);
}
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
for (int i = 0; i < 2; i ++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"2==-%@",[NSThread currentThread]);
}
dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
for (int i = 0; i < 2; i ++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"3==-%@",[NSThread currentThread]);
}
NSLog(@"group====end");
});
}
2019-07-17 16:07:42.775761+0800 YBStockChartView[2954:104854] currentThread===<NSThread: 0x600003cb6940>{number = 1, name = main}
2019-07-17 16:07:42.775845+0800 YBStockChartView[2954:104854] group--begin
2019-07-17 16:07:44.778363+0800 YBStockChartView[2954:104888] 2==-<NSThread: 0x600003cfa480>{number = 3, name = (null)}
2019-07-17 16:07:44.778373+0800 YBStockChartView[2954:104887] 1==-<NSThread: 0x600003cdcb00>{number = 4, name = (null)}
2019-07-17 16:07:46.783729+0800 YBStockChartView[2954:104887] 1==-<NSThread: 0x600003cdcb00>{number = 4, name = (null)}
2019-07-17 16:07:46.783748+0800 YBStockChartView[2954:104888] 2==-<NSThread: 0x600003cfa480>{number = 3, name = (null)}
2019-07-17 16:07:48.784472+0800 YBStockChartView[2954:104854] 3==-<NSThread: 0x600003cb6940>{number = 1, name = main}
2019-07-17 16:07:50.785241+0800 YBStockChartView[2954:104854] 3==-<NSThread: 0x600003cb6940>{number = 1, name = main}
2019-07-17 16:07:50.785515+0800 YBStockChartView[2954:104854] group====end
GCD信号量 dispatch_semaphone
将异步线程转换为同步线程 还可以利用信号量保证线程安全
- (void)semaphoneSync{
NSLog(@"currentThread===%@",[NSThread currentThread]);
NSLog(@"semaphone--begin");
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphone = dispatch_semaphore_create(0);
//创建一个 Semaphore 并初始化信号的总量
__block int number = 0;
dispatch_async(queue, ^{
//追加任务
[NSThread sleepForTimeInterval:2];
NSLog(@"1======%@",[NSThread currentThread]);
number =100;
//发送一个信号,让信号总量加 1
dispatch_semaphore_signal(semaphone);
});
//dispatch_semaphore_signal(semaphone);
//可以使总信号量减 1,信号总量小于 1 时就会一直等待(阻塞所在线程),否则就可以正常执行
dispatch_semaphore_wait(semaphone, DISPATCH_TIME_FOREVER);
NSLog(@"semaphore---end,num == %d",number);
}
2019-07-17 16:33:22.483822+0800 YBStockChartView[3373:125495] currentThread===<NSThread: 0x6000029fe940>{number = 1, name = main}
2019-07-17 16:33:22.483902+0800 YBStockChartView[3373:125495] semaphone--begin
2019-07-17 16:33:24.484306+0800 YBStockChartView[3373:125556] 1======<NSThread: 0x6000029a6a00>{number = 3, name = (null)}
2019-07-17 16:33:24.484620+0800 YBStockChartView[3373:125495] semaphore---end,num == 100
NSOperation
是苹果提供给开发者一套多线程解决方案,实际上基于GCD更高一层的封装,完全面向对象。但比GCD 更简单易用、代码可读性也更高
1、可以使用 KVO 观察对操作执行状态的更改:isExecuteing、isFinished、isCancelled。
2、设定操作执行的优先级
3、可以很方便的取消一个操作的执行
NSOperation、NSOperationQueue 操作和操作队列,
可以结合GCD的任务和任务队列理解
- 操作 operation
- 操作队列 (Operation Queues)
操作队列可以通过设置最大并发操作数(maxConcurrentOperationCount来控制并发、串行
NSOperationQueue 为我们提供了两种不同类型的队列:主队列和自定义队列。
主队列运行在主线程上,而自定义队列在后台执行
NSOperation、NSOperationQueue 使用步骤
NSOperation需要配合NSOperationQueue来实现多线程。默认情况下
NSOperation 单独使用时系统同步执行操作,配合NSOperationQueue 能更好的实现异步执行
分为三步
1、创建操作:先将需要执行的操作封装到一个NSOperation对象中
2、创建队列:创建NSOperationQueue 对象
3、将操作加入到队列中:将NSOperation对象添加到NSOperatinoQueue对象中
然后系统就会自动将NSOperationQueue 中的NSOperation 取出来,在新线程中执行操作。
NSOperation 和 NSOperationQueue 基本使用
创建
NSOperation是个抽象类,需要使用他的子类来封装操作,
- 1、使用子类NSInvocationOperation
- 2、使用子类NSBlockOperation
- 3、自定义继承自NSOperation的子类,实现内部方法来封装操作。
使用子类NSInvocationOperation
- (void)useInvocationOperation{
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];
[op start];
}
-(void)task1{
for (int i = 0; i < 5; i++) {
NSLog(@"1---%@",[NSThread currentThread]); //打印当前线程
}
}
2019-07-17 17:05:52.389871+0800 YBStockChartView[3820:146376] 1---<NSThread: 0x60000151e940>{number = 1, name = main}
2019-07-17 17:05:52.389995+0800 YBStockChartView[3820:146376] 1---<NSThread: 0x60000151e940>{number = 1, name = main}
2019-07-17 17:05:52.390056+0800 YBStockChartView[3820:146376] 1---<NSThread: 0x60000151e940>{number = 1, name = main}
2019-07-17 17:05:52.390132+0800 YBStockChartView[3820:146376] 1---<NSThread: 0x60000151e940>{number = 1, name = main}
2019-07-17 17:05:52.390232+0800 YBStockChartView[3820:146376] 1---<NSThread: 0x60000151e940>{number = 1, name = main}
上面打印可以看出,在没有使用NSOperationQueue,在主线程中单独使用子类 NSInvocationOperation 执行一个操作的情况下,操作是在当前线程执行的,并没有开启新线程
使用NSBlockOperation
- (void)useBlockOperation{
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 5; i++) {
NSLog(@"1---%@",[NSThread currentThread]); //打印当前线程
}
}];
[op start];
}
并没有开新线程,但是 NSBlockOperation 的addExecutionBlock 可以在开启新线程
- (void)useBlockOperation{
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 5; i++) {
NSLog(@"1---%@",[NSThread currentThread]); //打印当前线程
}
}];
[op addExecutionBlock:^{
for (int i = 0; i < 5; i++) {
NSLog(@"2---%@",[NSThread currentThread]); //打印当前线程
}
}];
[op addExecutionBlock:^{
for (int i = 0; i < 5; i++) {
NSLog(@"3---%@",[NSThread currentThread]); //打印当前线程
}
}];
[op addExecutionBlock:^{
for (int i = 0; i < 5; i++) {
NSLog(@"4---%@",[NSThread currentThread]); //打印当前线程
}
}];
[op addExecutionBlock:^{
for (int i = 0; i < 5; i++) {
NSLog(@"5---%@",[NSThread currentThread]); //打印当前线程
}
}];
[op start];
}
2019-07-17 17:13:33.847155+0800 YBStockChartView[3939:152105] 2---<NSThread: 0x600000794000>{number = 3, name = (null)}
2019-07-17 17:13:33.847161+0800 YBStockChartView[3939:152106] 5---<NSThread: 0x600000794040>{number = 6, name = (null)}
2019-07-17 17:13:33.847160+0800 YBStockChartView[3939:152071] 1---<NSThread: 0x6000007fe900>{number = 1, name = main}
2019-07-17 17:13:33.847160+0800 YBStockChartView[3939:152104] 3---<NSThread: 0x6000007a87c0>{number = 4, name = (null)}
2019-07-17 17:13:33.847162+0800 YBStockChartView[3939:152103] 4---<NSThread: 0x6000007b2c40>{number = 5, name = (null)}
2019-07-17 17:13:33.847288+0800 YBStockChartView[3939:152105] 2---<NSThread: 0x600000794000>{number = 3, name = (null)}
2019-07-17 17:13:33.847289+0800 YBStockChartView[3939:152104] 3---<NSThread: 0x6000007a87c0>{number = 4, name = (null)}
2019-07-17 17:13:33.847289+0800 YBStockChartView[3939:152071] 1---<NSThread: 0x6000007fe900>{number = 1, name = main}
2019-07-17 17:13:33.847290+0800 YBStockChartView[3939:152103] 4---<NSThread: 0x6000007b2c40>{number = 5, name = (null)}
2019-07-17 17:13:33.847296+0800 YBStockChartView[3939:152106] 5---<NSThread: 0x600000794040>{number = 6, name = (null)}
2019-07-17 17:13:33.847347+0800 YBStockChartView[3939:152105] 2---<NSThread: 0x600000794000>{number = 3, name = (null)}
2019-07-17 17:13:33.847372+0800 YBStockChartView[3939:152104] 3---<NSThread: 0x6000007a87c0>{number = 4, name = (null)}
2019-07-17 17:13:33.847430+0800 YBStockChartView[3939:152071] 1---<NSThread: 0x6000007fe900>{number = 1, name = main}
2019-07-17 17:13:33.847596+0800 YBStockChartView[3939:152103] 4---<NSThread: 0x6000007b2c40>{number = 5, name = (null)}
2019-07-17 17:13:33.847715+0800 YBStockChartView[3939:152106] 5---<NSThread: 0x600000794040>{number = 6, name = (null)}
2019-07-17 17:13:33.847797+0800 YBStockChartView[3939:152105] 2---<NSThread: 0x600000794000>{number = 3, name = (null)}
2019-07-17 17:13:33.847913+0800 YBStockChartView[3939:152104] 3---<NSThread: 0x6000007a87c0>{number = 4, name = (null)}
2019-07-17 17:13:33.847990+0800 YBStockChartView[3939:152071] 1---<NSThread: 0x6000007fe900>{number = 1, name = main}
2019-07-17 17:13:33.848107+0800 YBStockChartView[3939:152103] 4---<NSThread: 0x6000007b2c40>{number = 5, name = (null)}
2019-07-17 17:13:33.848199+0800 YBStockChartView[3939:152106] 5---<NSThread: 0x600000794040>{number = 6, name = (null)}
一般情况下,如果一个NSBlockOperation对象封装了多个操作,是否开启新线程,取决操作的个数,操作个数多就会开启多线程。具体个数有系统决定
使用自定义继承自NSOperatrion的子类 重写main方法
//NSOperation
- (void)main{
if (!self.isCancelled) {
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"PengOperation====%@", [NSThread currentThread]);
}
}
}
创建队列 NSOperationQueue
- 主队列
凡是添加到主队列中的操作,都会放到主线程中执行(注意:不包括操作使用addExecutionBlock:添加的额外操作)
NSOperationQueue *queue = [NSOperationQueue mainQueue];
- 非主队列
会自动放到子线程中执行
同时包含串性和并发
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
将操作加入到队列中
- (void)addOprationToQueue{
//创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//创建操作
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task2) object:nil];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 5; i++) {
NSLog(@"op3---%@",[NSThread currentThread]); //打印当前线程
}
}];
[op3 addExecutionBlock:^{
for (int i = 0; i < 5; i++) {
NSLog(@"other==op3---%@",[NSThread currentThread]); //打印当前线程
}
}];
[queue addOperation:op];
[queue addOperation:op2];
[queue addOperation:op3];
}
- (void)addOperationWithBlock:(void (^)(void))block;
无需先创建操作,在block中添加操作,直接将包含block加入到队列中
- (void)addOperationWithBlockToQueue{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
for (int i = 0; i < 4; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1----%@",[NSThread currentThread]);
}
}];
[queue addOperationWithBlock:^{
for (int i = 0; i < 4; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"2----%@",[NSThread currentThread]);
}
}];
}
2019-07-17 17:39:52.279263+0800 YBStockChartView[4380:172928] 2----<NSThread: 0x6000012da340>{number = 4, name = (null)}
2019-07-17 17:39:52.279275+0800 YBStockChartView[4380:172926] 1----<NSThread: 0x6000012cb500>{number = 3, name = (null)}
2019-07-17 17:39:54.279991+0800 YBStockChartView[4380:172926] 1----<NSThread: 0x6000012cb500>{number = 3, name = (null)}
2019-07-17 17:39:54.279997+0800 YBStockChartView[4380:172928] 2----<NSThread: 0x6000012da340>{number = 4, name = (null)}
2019-07-17 17:39:56.283783+0800 YBStockChartView[4380:172928] 2----<NSThread: 0x6000012da340>{number = 4, name = (null)}
2019-07-17 17:39:56.283804+0800 YBStockChartView[4380:172926] 1----<NSThread: 0x6000012cb500>{number = 3, name = (null)}
2019-07-17 17:39:58.285930+0800 YBStockChartView[4380:172926] 1----<NSThread: 0x6000012cb500>{number = 3, name = (null)}
2019-07-17 17:39:58.285930+0800 YBStockChartView[4380:172928] 2----<NSThread: 0x6000012da340>{number = 4, name = (null)}
NSOperatrion控制串行、并发执行---maxConcurrentOperationCount
注意:这里maxConcurrentOperationCount 控制的不是并发线程的数量,而是一个队列中同时能并发执行的最大操作数,而且一个操作也并非只能在一个线程中运行
默认情况maxConcurrentOperationCount = -1
- maxConcurrentOperationCount 为1时,队列为串行队列
- maxConcurrentOperationCount 大于1时,队列为并发队列
- (void)addOperationWithBlockToQueue{
NSLog(@"currentThread----%@",[NSThread currentThread]);
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//queue.maxConcurrentOperationCount = 1; //串行队列,切记并非只有一个线程,刻意有多个线程,但并发数最大为1
queue.maxConcurrentOperationCount = 6;
[queue addOperationWithBlock:^{
for (int i = 0; i < 4; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1----%@",[NSThread currentThread]);
}
}];
[queue addOperationWithBlock:^{
for (int i = 0; i < 4; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"2----%@",[NSThread currentThread]);
}
}];
[queue addOperationWithBlock:^{
for (int i = 0; i < 4; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"3----%@",[NSThread currentThread]);
}
}];
NSLog(@"currentThread---end----%@",[NSThread currentThread]);
}
NSOperatrion 操作依赖
- (void)addDependency{
//1/创建队列
NSLog(@"currentThread----%@",[NSThread currentThread]);
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//queue.maxConcurrentOperationCount = 1; //串行队列,切记并非只有一个线程,刻意有多个线程,但并发数最大为1
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 5; i++) {
NSLog(@"op3---%@",[NSThread currentThread]); //打印当前线程
}
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 5; i++) {
NSLog(@"op4---%@",[NSThread currentThread]); //打印当前线程
}
}];
[op3 addDependency:op4];
[queue addOperation:op3];
[queue addOperation:op4];
NSLog(@"currentThread---end----%@",[NSThread currentThread]);
}
2019-07-17 18:04:28.581372+0800 YBStockChartView[4808:189453] currentThread----<NSThread: 0x600002c42940>{number = 1, name = main}
2019-07-17 18:04:28.581706+0800 YBStockChartView[4808:189453] currentThread---end----<NSThread: 0x600002c42940>{number = 1, name = main}
2019-07-17 18:04:28.581756+0800 YBStockChartView[4808:189498] op4---<NSThread: 0x600002c1b0c0>{number = 3, name = (null)}
2019-07-17 18:04:28.581834+0800 YBStockChartView[4808:189498] op4---<NSThread: 0x600002c1b0c0>{number = 3, name = (null)}
2019-07-17 18:04:28.581897+0800 YBStockChartView[4808:189498] op4---<NSThread: 0x600002c1b0c0>{number = 3, name = (null)}
2019-07-17 18:04:28.581978+0800 YBStockChartView[4808:189498] op4---<NSThread: 0x600002c1b0c0>{number = 3, name = (null)}
2019-07-17 18:04:28.582085+0800 YBStockChartView[4808:189498] op4---<NSThread: 0x600002c1b0c0>{number = 3, name = (null)}
2019-07-17 18:04:28.582262+0800 YBStockChartView[4808:189494] op3---<NSThread: 0x600002c1d240>{number = 4, name = (null)}
2019-07-17 18:04:28.582342+0800 YBStockChartView[4808:189494] op3---<NSThread: 0x600002c1d240>{number = 4, name = (null)}
2019-07-17 18:04:28.593061+0800 YBStockChartView[4808:189494] op3---<NSThread: 0x600002c1d240>{number = 4, name = (null)}
2019-07-17 18:04:28.593160+0800 YBStockChartView[4808:189494] op3---<NSThread: 0x600002c1d240>{number = 4, name = (null)}
2019-07-17 18:04:28.593247+0800 YBStockChartView[4808:189494] op3---<NSThread: 0x600002c1d240>{number = 4, name = (null)}
NSOperation 、NSOperationQueue线程间的通信
- (void)communication{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
NSLog(@"1=====%@",[NSThread currentThread]);
}
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"主线程刷新UI%@",[NSThread currentThread]);
}];
}];
}
2019-07-17 20:57:03.534990+0800 YBStockChartView[5714:238159] 1=====<NSThread: 0x6000008fd100>{number = 3, name = (null)}
2019-07-17 20:57:03.535153+0800 YBStockChartView[5714:238159] 1=====<NSThread: 0x6000008fd100>{number = 3, name = (null)}
2019-07-17 20:57:03.535227+0800 YBStockChartView[5714:238159] 1=====<NSThread: 0x6000008fd100>{number = 3, name = (null)}
2019-07-17 20:57:03.537354+0800 YBStockChartView[5714:238126] 主线程刷新UI<NSThread: 0x6000008aa940>{number = 1, name = main}
NSOperation 常用属性和方法
- 取消操作方法
-(void)cancel; - 判断状态方法
-(BOOL)isFinished;
-(BOOL)isCancelled;
-(BOOL)isExecuting;
-(BOOL)isReady; - 操作同步
-(void)waitupntifinished 阻塞当前线程,知道该操作结束
-(void)addDependency:(NSOperation)op 添加依赖
-(void)removeDependency:(NSOperation)op 移除依赖
NSOperationQueue 常用属性和方法
- 1、取消/暂停/恢复操作
-(void)cancelAllOperations;可以取消队列的所有操作;
-(BOOL)isSuspended;判断队列是否处于暂停状态 - (void)setSuspended:(BOOL)b;可设置操作的暂停和恢复,YES 代表暂停队列,NO 代表恢复队列
- 2、操作同步
- (void)waitUntilAllOperationsAreFinished; 阻塞当前线程,直到队列中的操作全部执行完毕
- 3、添加/获取操作
- (void)addOperationWithBlock:(void (^)(void))block; 向队列中添加一个 NSBlockOperation 类型操作对象
- (void)addOperations:(NSArray *)ops waitUntilFinished:
-(BOOL)wait; 向队列中添加操作数组,wait 标志是否阻塞当前线程直到所有操作结束 - (NSArray *)operations; 当前在队列中的操作数组(某个操作执行结束后会自动从这个数组清除)
- (NSUInteger)operationCount; 当前队列中的操作数
- 4、获取队列
- (id)currentQueue; 获取当前队列,如果当前线程不是在 NSOperationQueue 上运行则返回 nil
- (id)mainQueue; 获取主队列
注意:
这里的暂停和取消(包括操作的取消和队列的取消)并不代表可以将当前的操作立即取消,而是当当前的操作执行完毕之后不再执行新的操作。
暂停和取消的区别就在于:暂停操作之后还可以恢复操作,继续向下执行;而取消操作之后,所有的操作就清空了,无法再接着执行剩下的操作
网友评论