多线程(归纳,总结)
总结一下多线程的东西,防止忘记。。。不然每次都得翻出来重新看
基础简介
-
进程
-
进程是指在系统中正在运行的一个应用程序
-
每个进程之间是独立的,每个进程均运行在某专用且受保护的内存空间内
-
线程
-
1个进程要想执行任务,必须得有线程存在(每一个进程至少有一个线程)
-
1个进程(程序)的所有任务都在线程里完成
线程的执行是串行的,一个一个按顺序执行
所以一般优化程序流畅度,就要开启多个线程,多个线程之间是并行的,可以分开处理不同的操作,一起执行
-
多线程的原理
-
同一时间,CPU只能处理1条线程,只有1条线程在工作(执行)
-
多线程并发(同时)执行,其实是CPU快速地在多线程之间调度(切换)
-
线程不能太多
-
如果线程过多,CPU调度不过来,会卡死
-
每条线程都会执行缓慢
-
多线程的优点
-
能适当提高程序的执行效率
-
能适当的提高资源利用率(CPU,内存的利用率,不会空转)
-
多线程的缺点
-
创建线程是有开销的,iOS主要成本包括:
- 内核数据结构(大约1KB)
- 栈空间(子线程512KB,主线程1MB,也可以使用-setStackSize:设置,但必须是4K的倍数,而且最小是16K,不建议自己设定,系统既然设定了就是有用的),创建线程大约需要90毫秒的创建时间
- 线程开启太多,会降低程序的性能,iOS一般最多5条线程
- 线程越多,CPU在调度线程上的开销越大
- 程序设计更加复杂:比如线程之间的通信,多线程的数据共享
-
主线程
-
一个程序运行后,默认会开启一条线程,成为"主线程"或者"UI线程"
-
主线程的作用
-
显示\刷新UI界面
-
处理UI时间(比如点击事件\滚动事件\拖拽事件等)
-
主线程的使用注意
-
耗时操作放在子线程,不要放在主线程
四中多线程的实现
Snip20160321_2.pngpthead
纯C语言的,这玩意儿基本上不用
创建代码
// 第一个传一个
pthread_create(<#pthread_t *restrict#>, <#const pthread_attr_t *restrict#>, <#void *(*)(void *)#>, <#void *restrict#>)
第一个参数需要创建pthread_t对象地址,第三个参数传一个C语言函数,第二个和第四个参数传NULL
// 例子
pthread_t thread2;
pthread_create(&thread2, NULL, buttonClick, NULL);
void * buttonClick(void *param)
{
for (NSInteger i = 0; i<10000; i++) {
NSLog(@"------buttonClick---%zd--%@", i, [NSThread currentThread]);
}
return NULL;
}
这个C的东西,反正我没用过...
NSThread
这种多线程的方法是比较轻量级,全程自己手动管理生命周期,同步,加锁等问题,封装了C语言的多线程
- 动态方法
/**
* selector : 线程执行的方法,这个selector最多只能接收一个参数
* target : selector是消息发送的对象,发给谁的
* argument : 传给selector的唯一参数
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument;
// 例子,线程的开启
// 初始化线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
// 设置线程的优先级(0.0 ~ 1.0,1.0是最高级)
thread.threadPriority = 1;
// 设置线程的名字
thread.name = @"jack";
// 开启线程
[thread start];
- 其他创建多线程的方法
- 静态方法
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;
// 例子
// 调用完毕后,会马上创建并开启新线程
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
- 隐式创建线程
[self performSelectorInBackground:@selector(run) withObject:nil]
优点: 简单快捷
缺点:无法对线程进行更详细的设置
相关方法
+ (NSThread *)mainThread; // 获得主线程
- (BOOL)isMainThread; // 是否为主线程
+ (BOOL)isMainThread; // 是否为主线程
获得当前线程
NSThread *current = [NSThread currentThread];
线程的名字
- (void)setName:(NSString *)n;
- (NSString *)name;
控制线程的状态
- 启动线程
// 进入就绪状态->运行状态-- 执行完毕 -- > 死亡状态
- (void)start;
- 阻塞(暂停)线程
- (void)sleepUntilDate:(NSDate *)date;
// 例子
// [NSThread sleepUntilDate:[NSDate distantFuture]]; // [NSDate distantFuture]从现在开始到永远
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];
- (void)sleepForTimeInterval:(NSTimeInterval)ti;
[NSThread sleepForTimeInterval:2]; // 让线程睡眠2秒(阻塞2秒)
- 强制死亡状态
// 强制停止线程
+ (void)exit;
一旦线程死亡,就必须重新开启新的线程,不能重新调用
线程锁
在多个线程同时访问一个对象的时候,同时对该对象进行了操作,就会出现安全隐患,用NSThread的方法需要自己管理线程锁,这就是互斥锁
- 互斥锁的使用格式
@synchronized(锁对象) { // 需要锁定的代码 }
// 一份代码只有一把锁,多把锁是无效的,还是乱的
// 这个锁一般是全局变量,表示唯一的(切记)
互斥锁的优缺点
优点:能有效防止因多线程抢夺资源造成的数据安全问题
缺点:需要消耗大量的CPU资源
加互斥锁也就是要打到"线程同步"的问题
线程同步的意思是:多条线程在同一条线上执行(按顺序地执行任务)
互斥锁,就是使用了线程同步技术
意思就是说所有线程上同一对象是一样的!!!!
线程之间的通信
在一个进程中,多个线程之间是有一定关联,相互通信的
- 一般在子线程进行复杂操作,在主线程刷新UI,这就是线程通信
常用方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
// 最后这个(BOOL)wait是指下面代码是否等这个执行完了之后再进行
- 苹果还提供了第二种线程同步的方法NSMachPort
NSPort有3个子类:NSSocketPort、NSMessagePort、NSMachPort,但在iOS下只有NSMachPort可用。
使用的方式为接收线程中注册NSMachPort,在另外的线程中使用此port发送消息,则被注册线程会收到相应消息,然后最终在主线程里调用某个回调函数。
可以看到,使用NSMachPort的结果为调用了其它线程的1个函数,而这正是performSelector所做的事情,所以,NSMachPort是个鸡肋。线程间通信应该都通过performSelector来搞定。
GCD
Grand Central Dispatch(中央调度)
纯C语言,对NSThread进行了封装
切记:
不要在当前穿行队列里使用sync函数,因为会卡住线程,调用sync函数,系统会马上去执行这个函数,但是当前线程也是串行的,他会默认执行完当前线程再去执行别的,这就造成了冲突,会卡死
GCD优势:
- 为多核的并发运算提出的解决方法
- 自动利用更多的CPU内核(比如双核,四核)
- 自动管理线程的生命周期(创建线程,调度任务,销毁线程等)
- 只需要告诉GCD执行什么,GCD会自行管理任务
GCD两个核心概念
- 任务:执行什么操作
- 队列:用来存放任务
GCD的使用就2个步骤
- 定制任务
- 确定想做的事情
将任务添加到队列中
- GCD会自动将队列中的任务取出,放到对应的线程中执行
- 任务的取出遵循队列的FIFO原则:先进先出,后进后出
GCD的OC对象的内存管理
GCD支持Cocoa内存管理机制,因此可以在提交到queue的block中自由地使用Objective-C对象。每个dispatch queue维护自己的autorelease pool确保释放autorelease对象,但是queue不保证这些对象实际释放的时间。如果应用消耗大量内存,并且创建大量autorelease对象,你需要创建自己的autorelease pool,用来及时地释放不再使用的对象。
同步和异步的区别
- 同步:只能在当前线程中执行任务,不具备开启线程的能力
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
- 异步:可以在新的线程中执行任务,具备开启线程的能力
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
Snip20160321_7.png
例子
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self syncConcurrent];
}
/**
* 同步函数 + 主队列:
*/
- (void)syncMain
{
NSLog(@"syncMain ----- begin");
// 1.获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.将任务加入队列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
NSLog(@"syncMain ----- end");
}
/**
* 异步函数 + 主队列:只在主线程中执行任务
*/
- (void)asyncMain
{
// 1.获得主队列
dispatch_queue_t queue = dispatch_get_main_queue();
// 2.将任务加入队列
dispatch_async(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
}
/**
* 同步函数 + 串行队列:不会开启新的线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务
*/
// 俩参数,第一个是名字,第二个是执行的优先级
/**
* 全局并发队列的优先级
* #define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高
* #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认(中)
* #define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低
* #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台
*/
- (void)syncSerial
{
// 1.创建串行队列
dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_SERIAL);
// 2.将任务加入队列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
}
/**
* 异步函数 + 串行队列:会开启新的线程,但是任务是串行的,执行完一个任务,再执行下一个任务
*/
- (void)asyncSerial
{
// 1.创建串行队列
// 两个参数,第一个是名字,第二个是穿行队列
// DISPATCH_QUEUE_CONCURRENT 并行队列
// DISPATCH_QUEUE_SERIAL 串行队列
dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_SERIAL);
// 默认是穿行队列,所以第二个参数传NULL也行
![Uploading Snip20160321_7_826971.png . . .]
// dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", NULL);
// 2.将任务加入队列
dispatch_async(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
}
/**
* 同步函数 + 并发队列:不会开启新的线程
* 因为还是同步函数,所以你的并发执行并没有什么卵用
*/
- (void)syncConcurrent
{
// 1.获得全局的并发队列
// 在你程序启动的时候,实际上GCD已经给你创建了几个全局的队列,你拿来用就好了,所以其实可以不用自己创建
// 俩参数,第一个为线程的优先级,第二个为预留参数,传0就行了
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.将任务加入队列
dispatch_sync(queue, ^{
NSLog(@"1-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2-----%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3-----%@", [NSThread currentThread]);
});
NSLog(@"syncConcurrent--------end");
}
/**
* 异步函数 + 并发队列:可以同时开启多条线程
*/
- (void)asyncConcurrent
{
// 1.创建一个并发队列
// label : 相当于队列的名字
// 俩参数,第一个是线程的名字,第二个是优先级
// dispatch_queue_t queue = dispatch_queue_create("com.520it.queue", DISPATCH_QUEUE_CONCURRENT);
// 1.获得全局的并发队列
// 在你程序启动的时候,实际上GCD已经给你创建了几个全局的队列,你拿来用就好了,所以其实可以不用自己创建
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 2.将任务加入队列
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);
}
GCD里面线程之间的通信
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 图片的网络路径
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
// 加载图片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成图片
UIImage *image = [UIImage imageWithData:data];
// 回到主线程 (通信)
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});
}
GCD的其他使用
- barrier障碍
把几个线程分开,先执行障碍之前的代码,然后执行障碍,最后执行障碍以后的代码
dispatch_barrier_async(<#dispatch_queue_t queue#>, <#^(void)block#>)
例子
- (void)barrier
{
// dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----2-----%@", [NSThread currentThread]);
});
// 设置障碍
dispatch_barrier_async(queue, ^{
NSLog(@"----barrier-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----3-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----4-----%@", [NSThread currentThread]);
});
}
- 延迟执行
dispatch_time延迟执行的代码在block里面,第一个参数为开始时间,第二个参数为延迟的秒数,单位为纳米,转化单位NSEC_PER_SEC ,第三个参数为执行的线程
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(<#delayInSeconds#> * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
<#code to be executed after a specified delay#>
});
例子
- (void)delay
{
NSLog(@"touchesBegan-----");
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"run-----");
});
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];
}
- (void)run
{
NSLog(@"run-----");
}
- 一次性代码(单例要用)
// 一次执行代码,这个代码在这一整个进程里就执行一次,不会执行第二次
- (void)once
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"------run");
});
}
- 快速迭代(我感觉就是遍历)
// 用GCD
- (void)apply
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSString *from = @"/Users/xiaomage/Desktop/From";
NSString *to = @"/Users/xiaomage/Desktop/To";
NSFileManager *mgr = [NSFileManager defaultManager];
NSArray *subpaths = [mgr subpathsAtPath:from];
// 快速迭代 block里面有个参数为size_t index
dispatch_apply(subpaths.count, queue, ^(size_t index) {
NSString *subpath = subpaths[index];
NSString *fromFullpath = [from stringByAppendingPathComponent:subpath];
NSString *toFullpath = [to stringByAppendingPathComponent:subpath];
// 剪切
[mgr moveItemAtPath:fromFullpath toPath:toFullpath error:nil];
NSLog(@"%@---%@", [NSThread currentThread], subpath);
});
}
/**
* 对比:传统文件剪切
*/
- (void)moveFile
{
NSString *from = @"/Users/xiaomage/Desktop/From";
NSString *to = @"/Users/xiaomage/Desktop/To";
NSFileManager *mgr = [NSFileManager defaultManager];
NSArray *subpaths = [mgr subpathsAtPath:from];
for (NSString *subpath in subpaths) {
NSString *fromFullpath = [from stringByAppendingPathComponent:subpath];
NSString *toFullpath = [to stringByAppendingPathComponent:subpath];
// 复杂耗时操作放在子线程 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 剪切
[mgr moveItemAtPath:fromFullpath toPath:toFullpath error:nil];
});
}
}
- 设置依赖分组关系
- 创建队列
- 创建group组对象dispatch_group_create()
- 讲操作加到组里
- 在组里完成所有操作后,通过
dispatch_notify(<#object#>, <#queue#>, <#notification_block#>)
方法执行下一步操作,有三个参数,第一个是哪一组的,第二个是在哪个队列,第三个是要执行的方法block
- (void)group
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 创建一个队列组
dispatch_group_t group = dispatch_group_create();
// 1.下载图片1
dispatch_group_async(group, queue, ^{
// 图片的网络路径
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
// 加载图片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成图片
self.image1 = [UIImage imageWithData:data];
});
// 2.下载图片2
dispatch_group_async(group, queue, ^{
// 图片的网络路径
NSURL *url = [NSURL URLWithString:@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];
// 加载图片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成图片
self.image2 = [UIImage imageWithData:data];
});
// 3.将图片1、图片2合成一张新的图片
dispatch_group_notify(group, queue, ^{
// 开启新的图形上下文
UIGraphicsBeginImageContext(CGSizeMake(100, 100));
// 绘制图片
[self.image1 drawInRect:CGRectMake(0, 0, 50, 100)];
[self.image2 drawInRect:CGRectMake(50, 0, 50, 100)];
// 取得上下文中的图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 结束上下文
UIGraphicsEndImageContext();
// 回到主线程显示图片
dispatch_async(dispatch_get_main_queue(), ^{
// 4.将新图片显示出来
self.imageView.image = image;
});
});
}
NSOperation
NSOperation的作用:配合使用NSOperation和NSOperationQueue也能实现多线程编程
NSOperation和NSOperationQueue实现多线程的具体步骤
- 先将需要执行的操作封装到一个NSOperation对象中
- 然后将NSOperation对象添加到NSOperationQueue中
- 系统会自动将NSOperationQueue中的NSOperation取出来
- 将取出的NSOperation封装的操作放到一条新线程中执行
NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类
使用NSOperation子类的方式有3种
- NSInvocationOperation(不常用)
- NSBlockOperation
- 自定义子类继承NSOperation,实现内部相应的方法
NSInvocationOperation
- (void)invocationOperation
{
// 创建NSInvocationOperation对象
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
// 调用start方法开始执行操作
[op start];
}
- 默认情况下,调用start方法后并不会开一条新线程去执行操作,而是在当前线程同步进行操作
- 只有讲NSOperation放到一个NSOperationQueue中,才会异步执行操作
NSBlockOperation
在NSBlockOperation操作里,只要封装的任务个数大于1,就会异步执行
- (void)blockOperation
{
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// 在主线程
NSLog(@"下载1------%@", [NSThread currentThread]);
}];
// 添加额外的任务(在子线程执行)
[op addExecutionBlock:^{
NSLog(@"下载2------%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"下载3------%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"下载4------%@", [NSThread currentThread]);
}];
[op start];
}
NSOperationQueue
NSOperation一般是和NSOperationQueue配合使用的
在NSOperation调用start方法的时候,如果没有加入NSOperationQueue,那么他们是同步在当前线程执行的,只有加入了NSOperationQueue,系统会自动给你异步执行,而且怎么异步,开多少线程都不用你管
两种方式
- (void)addOperation:(NSOperation *)op;
- (void)addOperationWithBlock:(void (^)(void))block;
示例如下
```objc
- (void)operationQueue1
{
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 创建操作(任务)
// 创建NSInvocationOperation
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download1) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download2) object:nil];
// 创建NSBlockOperation
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]);
}];
// 创建XMGOperation
XMGOperation *op5 = [[XMGOperation alloc] init];
// 添加任务到队列中
[queue addOperation:op1]; // [op1 start]
[queue addOperation:op2]; // [op2 start]
[queue addOperation:op3]; // [op3 start]
[queue addOperation:op4]; // [op4 start]
[queue addOperation:op5]; // [op5 start]
}
- (void)operationQueue2
{
// 创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 创建操作
// NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
// NSLog(@"download1 --- %@", [NSThread currentThread]);
// }];
// 添加操作到队列中
// [queue addOperation:op1];
[queue addOperationWithBlock:^{
NSLog(@"download1 --- %@", [NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"download2 --- %@", [NSThread currentThread]);
}];
}
- 最大并发数
可以设置最大并发数,也就是最多开启的线程数
把最大并发数设置成1,就变成了串行
- (NSInteger)maxConcurrentOperationCount;
- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
- 取消队列的所有操作
- (void)cancelAllOperations;```
提示:也可以调用NSOperation的- (void)cancel方法取消单个操作
- 暂停和恢复队列
```objc
- (void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列
- (BOOL)isSuspended;
- 线程之间的依赖
这个设置起来比着GCD要简单的多,GCD可以通过barrier,建组的方式,然而,NSOperation却只需要一行代码,而且还可以设置不同线程之间的依赖等
- (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.completionBlock = ^{
NSLog(@"op5执行完毕---%@", [NSThread currentThread]);
};
// 设置依赖
[op3 addDependency:op1];
[op3 addDependency:op2];
[op3 addDependency:op4];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue addOperation:op4];
[queue addOperation:op5];
}
同样是下载图片的依赖关系
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 想让block里面改外面的局部变量的值,在外部参数加__block
__block UIImage *image1 = nil;
// 下载图片1
NSBlockOperation *download1 = [NSBlockOperation blockOperationWithBlock:^{
// 图片的网络路径
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
// 加载图片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成图片
image1 = [UIImage imageWithData:data];
}];
__block UIImage *image2 = nil;
// 下载图片2
NSBlockOperation *download2 = [NSBlockOperation blockOperationWithBlock:^{
// 图片的网络路径
NSURL *url = [NSURL URLWithString:@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];
// 加载图片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成图片
image2 = [UIImage imageWithData:data];
}];
// 合成图片
NSBlockOperation *combine = [NSBlockOperation blockOperationWithBlock:^{
// 开启新的图形上下文
UIGraphicsBeginImageContext(CGSizeMake(100, 100));
// 绘制图片
[image1 drawInRect:CGRectMake(0, 0, 50, 100)];
image1 = nil;
[image2 drawInRect:CGRectMake(50, 0, 50, 100)];
image2 = nil;
// 取得上下文中的图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 结束上下文
UIGraphicsEndImageContext();
// 回到主线程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
}];
}];
[combine addDependency:download1];
[combine addDependency:download2];
[queue addOperation:download1];
[queue addOperation:download2];
[queue addOperation:combine];
}
- 线程之间的通讯
- (void)test
{
[[[NSOperationQueue alloc] init] addOperationWithBlock:^{
// 图片的网络路径
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
// 加载图片
NSData *data = [NSData dataWithContentsOfURL:url];
// 生成图片
UIImage *image = [UIImage imageWithData:data];
// 回到主线程
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
self.imageView.image = image;
}];
}];
}