多线程以及底层实现
来源:互联网 发布:ssm 怎么打印sql 编辑:程序博客网 时间:2024/05/22 18:47
一.概念
- 什么是进程
- 进程是指在系统中正在运行的一个应用程序
- 每个进程之间的是独立的,每个进程均运行在其专用且受保护的内存空间内
- 一个进程至少要有一个线程
- 什么是线程
- 一个线程要执行任务,必须得有线程
- 一个进程(程序)的所有任务都在线程中执行的
- 一个线程执行任务是串行的,也就是说一个线程,同一时间内,只能执行一个任务
- 多线程原理
- 同一时间,CPU只能处理1条线程,只有一条线程在工作(执行)
- 多线程并发(同时)执行,其实质是CPU快速的在多线程之间调度(切换)
- 如果线程过多,会怎样?
- CPU在N多条线程中调度,会消耗大量的cpu资源
- 每条线程被调度执行的频率越低(线程的执行效率低)
- 多线程的优点
- 能适当提高程序的执行效率
- 能适当提高资源的利用率(CPU 内存利用率等)
- 多线程的缺点
- 创建线程是有开销的,iOS下主要成本包括:内核数据结构(大约1KB)、栈空间(子线程512KB、主线程1MB,也可以使用-setStackSize:设置,但必须是4K的倍数,而且最小是16K),创建线程大约需要90毫秒的创建时间
- 如果开启大量的线程,会降低程序的性能
- 程序越多CPU的线程上的开销就越大
- 程序设计更加复杂:线程之间的通讯,多线程的数据共享
- 什么是主线程
- 一个ios程序运行后,会开启一条线程,称为主线程,或者是UI线程
- 主线程的主要作用
- 显示和刷新UI界面
- 处理UI事件(比如点击事件,滚动事件,拖拽事件等)
- 主线程的使用注意
- 别将比较耗时的操作放在主线程中,会导致UI界面的卡顿
- 将耗时操作放在子线程(后台线程,非主线程)
二.多线程的4种方案
1.C语言的POSIX接口:#include
+ (NSThread *)mainThread; // 获得主线程- (BOOL)isMainThread; // 是否为主线程+ (BOOL)isMainThread; // 是否为主线程```- 其他用法 ```objc//创建NSThread对象 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:nil object:nil]; //获取当前线程 NSThread *thread1 = [NSThread currentThread]; //设置线程的名字 thread.name = @"这是设置线程名字的"; [thread setName:@"这也是设置线程的名字的"];```- 线程状态![这里写图片描述](http://img.blog.csdn.net/20160615133614638)- 互斥锁 - @synchronized(锁对象) { // 需要锁定的代码 }注意:锁定1份代码只用1把锁(同个对象),用多把锁是无效的 - 优点:能有效防止因多线程抢夺资源造成的额数据安全问题 - 缺点:需要消耗大量的CPU资源 - 使用前提:多线程同一时间抢夺同一块资源 - 互斥锁就是使用了线程同步技术.就是多线程在按顺序执行- OC在定义属性时有nonatomic和atomic两种选择atomic:原子属性,为setter方法加锁(默认就是atomic)nonatomic:非原子属性,不会为setter方法加锁###1.常用的方法```objc//在主线程中执行任务- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array;//YES表示等Selector执行完后才执行后面- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;//开辟一条子线程执行任务- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray<NSString *> *)array //开辟一条子线程执行任务- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait //在后台执行任务(相当于开辟了一个子线程)- (void)performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg //类方法,开辟一条子线程执行任务[NSThread detachNewThreadSelector:@selector(test:) toTarget:self withObject:@"hello"];<div class="se-preview-section-delimiter"></div>
//开启子线程,并且要通过手动开启线程,否则无法执行任务的
// 创建线程 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(download:) object:@"Alloc"]; // 开启线程,将线程添加到线程可调度池里,等待CPU的调度 [thread start];<div class="se-preview-section-delimiter"></div>
//线程睡眠
//睡眠1秒[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];//线程睡眠1秒[NSThread sleepForTimeInterval:1.0];<div class="se-preview-section-delimiter"></div>
//线程退出,千万不要在主线程退出,否则程序就会出现各种问题,包括奔溃,黑屏,不能响应
[NSThread exit];<div class="se-preview-section-delimiter"></div>
2.线程的取消
// 创建线程--> 新建状态 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(download) object:nil]; // 开启线程--> 将线程添加到可调度线程池中,等待CPU调度 [thread start]; // 睡 [NSThread sleepForTimeInterval:0.2]; // 取消线程执行,取消线程仅仅是给线程打上一个被取消的标记 // 要实现真正取消线程的执行需要在线程内部的关键节点进行判断 [thread cancel]; NSLog(@"over");<div class="se-preview-section-delimiter"></div>
3.线程的优先级
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(test:) object:@"---"]; thread.name = @"线程A"; //设置线程的优先级,范围是0到1 thread.threadPriority = 1.0; [thread start];<div class="se-preview-section-delimiter"></div>
4.线程栈区的大小
// 不管主线程还是子线程,栈区大小默认都是512kb. // NSLog(@"stackSize = %zd",[NSThread currentThread].stackSize / 1024); // 最小的栈区大小是16kb,必须是4的整数倍,建议不要修改栈区大小,使用默认即可. [NSThread currentThread].stackSize = 1024 * 1024; NSLog( @"stackSize = %zd",[NSThread currentThread].stackSize / 1024)<div class="se-preview-section-delimiter"></div>
5.多线程访问数据导致的数据异常的处理
多个线程访问数据的时候会导致数据异常,这时候我们要使用互斥锁
// 先保证单条线程能正确工作:能够买完所有票- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { self.ticket = 20; // 创建线程 NSThread *threadA = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil]; threadA.name = @"美女A"; // 开启线程 [threadA start]; // 创建线程 NSThread *threadB = [[NSThread alloc] initWithTarget:self selector:@selector(saleTicket) object:nil]; threadB.name = @"美女B"; // 开启线程 [threadB start];}- (void)saleTicket { while (YES) { // 睡 [NSThread sleepForTimeInterval:1.0]; // 使用互斥锁:可以保证锁住的代码同一时间只有一个线程访问. // 使用互斥锁锁住的代码要尽可能少,锁住关键节点的代码即可. // [[NSUserDefaults standardUserDefaults] synchronize]; //@synchronized // self:锁对象,可以是任意NSObject类的对象. // 注意点:锁对象必须是所有线程对象能同时访问的对象. // 如果只有一个地方使用到互斥锁,一般锁对象就是self`,避免再创建一个对象. // NSObject *lockObj = [[NSObject alloc] init]; @synchronized(self) { // 判断是否有剩余的票数 if(self.ticket > 0) { // 如果有,则卖一张 self.ticket --; NSLog(@"%@卖了一张票,剩余的票数:%zd",[NSThread currentThread].name, self.ticket); continue; } } // 没有票,则提示用户票没了 NSLog(@"票没了"); break; } NSLog(@"over");}<div class="se-preview-section-delimiter"></div>
拓展:
•nonatomic 与 atomic1)iOS中还有一种锁 原子锁atomic2)nonatomic 非原子属性 (线程不安全),3)atomic 原子属性,默认都是"原子"属性 (线程安全),也不能保证数据写入的正确性 4)原子属性,也是一个多线程技术,setter/getter函数是一个原子操作,如果多线程同时调用setter时,不会出现某一个线程执行完setter所有语句之前,另一个线程就开始执行setter,相当于函数头尾 加了锁. 这样的话并发访问性能会比较低.提示:设置原子属性后,不要自己去写原子属性的setter方法原因:原子属性默认的setter方法中,使用了“128位自旋锁”,性能比互斥锁高,但是同样消耗性 能<div class="se-preview-section-delimiter"></div>
- ##3.C语言的GCD(性能好,代码更精简)
- 什么是GCD
- 全称是 Grand Central Dispatch,可译为“牛逼的中枢调度器”
- 纯C语言,提供了非常多强大的函数
- GCD的优势
- GCD是苹果公司为多核的并行运算提出的解决方案
- GCD会自动利用更多的CPU内核(比如双核、四核)
- GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
- 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
- GCD的有四种队列:串行队列,并行队列,全局队列,主队列.
- GCD有两种操作:异步操作,同步操作.
- 异步操作dispatch_async,”会并发执行”,无法确定任务的执行顺序
- 同步操作dispatch_sync,”会依次顺序执行”,能够决定任务的执行顺序
- 主队列可以看成串行队列,如果在主线程中使用同步操作,那就会造成线程阻塞
- 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程,线程是操作
系统可识别的最小执行和调度单位.
1.CGD的常用使用
注意:在主线程中,执行同步操作,里面的任务将不会执行(阻塞),同步异步决定是否可以开子线程,队列决定任务是怎么执行的.
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{// [self gcdDemo1];//cgd的同步和异步执行任务.// [self gcdDemo2];// [self gcdDemo3]; }//cgd的同步和异步执行任务.-(void)gcdDemo1{ void (^gcdBlock)() = ^() { NSLog(@"这是简单的block,%@",[NSThread currentThread]); }; dispatch_queue_t queue = dispatch_get_global_queue(0, 0); dispatch_sync(queue, gcdBlock);//开启子线程 dispatch_async(queue, gcdBlock);//不开启子线程}//gcd的常用写法(精简版)- (void)gcdDemo2{ //在全局队列异步执行 dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"async---%@",[NSThread currentThread]); }); //在主队列中异步执行.(没有开启子线程) dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"async---%@",[NSThread currentThread]); }); //在主队列同步执行 dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"sync---%@",[NSThread currentThread]); });}//线程间的通讯- (void)gcdDemo3{ //在异步开启子线程 dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"这是耗时任务---%@",[NSThread currentThread]); //异步在主线程执行任务 dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"回到主线程刷新UI--%@",[NSThread currentThread]); }); });}<div class="se-preview-section-delimiter"></div>
2.CGD中的串行队列
/// 串行队列异步执行/// 提问:会不会开线程? 是否是顺序执行?/// 问答:会开线程 开多条 是- (void)gcdDemo3{ for (int i = 0; i < 10; i++) { // 参数1:队列的名称 dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL); // 将任务添加到队列中,并指定执行任务的函数 dispatch_async(queue, ^ { NSLog(@"%@--%d",[NSThread currentThread],i); }); }}/// 串行队列同步执行/// 提问:会不会开线程? 是否是顺序执行?/// 问答:不会开线程 是- (void)gcdDemo1 { // 创建串行队列 // 参数1:队列的名称 dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL); for (int i = 0; i < 10; ++i) { // 将任务添加到队列中,并指定执行任务的函数 dispatch_sync(queue, ^ { NSLog(@"%@--%d",[NSThread currentThread],i); }); }}/// 串行队列异步执行/// 提问:会不会开线程? 开几条线程? 是否是顺序执行?/// 问答:会开线程 开1条 是 全对- (void)gcdDemo2 { // 创建串行队列 // 参数1:队列的名称 dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL); for (int i = 0; i < 10; ++i) { // 将任务添加到队列中,并指定执行任务的函数 dispatch_async(queue, ^ { NSLog(@"%@--%d",[NSThread currentThread],i); }); }}<div class="se-preview-section-delimiter"></div>
3.GCD中的并发队列
/// 并发队列同步执行/// 提问:会不会开线程? 开几条? 是否是顺序执行?/// 答案:不会开 顺序- (void)gcdDemo1 { // 创建串行队列 // 参数1:队列的名称 for (int i = 0; i < 10; ++i) { // 将任务添加到队列中,并指定执行任务的函数 dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_CONCURRENT); dispatch_sync(queue, ^ { NSLog(@"%@--%d",[NSThread currentThread],i); }); }}/// 串行队列异步执行/// 提问:会不会开线程? 开几条线程? 是否是顺序执行?/// 问答:会开线程 不知道,由底层线程池决定 不是- (void)gcdDemo2 { // 创建串行队列 // 参数1:队列的名称 dispatch_queue_t queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i < 5; ++i) { // 将任务添加到队列中,并指定执行任务的函数 dispatch_async(queue, ^ { NSLog(@"%@--%d",[NSThread currentThread],i); }); } NSLog(@"下载xxxx=%@",[NSThread currentThread]); NSLog(@"下载B=%@",[NSThread currentThread]); dispatch_async(queue, ^{ NSLog(@"下载C=%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"下载A=%@",[NSThread currentThread]); });}<div class="se-preview-section-delimiter"></div>
4.GCD中的主队列(特殊的串行队列)
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSLog(@"start"); [self gcdDemo3]; NSLog(@"end");}///主队列同步执行不死锁- (void)gcdDemo3 { // 获得主队列 dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"over = %@",[NSThread currentThread]); // 同步任务 dispatch_sync(queue, ^{ NSLog(@"%@---%zd",[NSThread currentThread],100); }); });}///主队列同步执行- (void)gcdDemo2 { // 获得主队列 dispatch_queue_t queue = dispatch_get_main_queue(); // 同步任务 // 主队列中的任务必须在主线程空闲的时才会执行. // 主队列中添加同步任务会造成死锁. dispatch_sync(queue, ^{ NSLog(@"%@---%zd",[NSThread currentThread],100); }); NSLog(@"over");}//主队列异步执行- (void)gcdDemo1 { // 获得主队列 dispatch_queue_t queue = dispatch_get_main_queue(); for (int i = 0; i < 5; ++i) { // 异步任务 dispatch_async(queue, ^{ NSLog(@"%@---%zd",[NSThread currentThread],i); }); }}<div class="se-preview-section-delimiter"></div>
5.GCD中的全局队列
//全局队列异步执行//全局队列特点跟并发队列是一样的// dispatch_get_global_queue:获得全局队列不需要关心什么时候销毁// 自己创建的并发队列需要在不使用的时候销毁// 一般开发第三方框架时会是用自定义并发队列.- (void)gcdDemo1 { // 获得主队列// Flags that are reserved for future use. Always specify 0 for this parameter // 参数1: // 参数2:保留给未来使用,永远给个0 dispatch_queue_t queue = dispatch_get_global_queue(0, 0); for (int i = 0; i < 10; ++i) { // 异步任务 dispatch_async(queue, ^{ NSLog(@"%@---%zd",[NSThread currentThread],i); }); } // 在ARC中不允许调用release方法// dispatch_release(queue);}<div class="se-preview-section-delimiter"></div>
6.CGD中的dispatch_barrier_async,barrier异步的使用
使用 dispatch_barrier_async 添加的 block 会在之前添加的 block 全部运行结束之后,统一在同一个线程顺序执行,从而保证对非线程安全的对象进行正确的操作!
- (void)viewDidLoad { [super viewDidLoad]; // 创建并发队列 queue = dispatch_queue_create("itcast", DISPATCH_QUEUE_CONCURRENT); // dispatch_barrier_async:一定是要使用自定义并发队列// queue = dispatch_get_global_queue(0, 0); for (int i = 0; i < 20; ++i) { [self loadImage:i]; }}/// 加载index索引指定图片// 主要用于在多个异步操作完成之后,统一对非线程安全的对象进行更新- (void)loadImage:(NSInteger)index{ dispatch_async(queue, ^{ // 获得图片名称 NSString *imageName = [NSString stringWithFormat:@"%zd.jpg",index % 9]; // 获得图片路径 NSString *filePath = [[NSBundle mainBundle] pathForResource:imageName ofType:nil]; // 加载图片 UIImage *image = [UIImage imageWithContentsOfFile:filePath]; NSLog(@"下载第%zd张图片,%@",index,[NSThread currentThread]); dispatch_barrier_async(queue, ^{ NSLog(@"第%ld张图片下载完成=%@,",(long)index,[NSThread currentThread]); // 将图片添加到数组中 // NSMutableArray不是线程安全的类 // NSMutableDictionary也不是线程安全的类的 // 凡是带有mutable单词的类都不是线程安全. [self.images addObject:image]; }); });}<div class="se-preview-section-delimiter"></div>
7.dispatch_after操作和dispatch_once
1.dispatch_after
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4.0 * NSEC_PER_SEC)); //延时操作,这里是开启了子线程的 dispatch_after(when, dispatch_get_global_queue(0, 0), ^{ NSLog(@"延迟操作是子线程:%@",[NSThread currentThread]); NSLog(@"这是延迟操作!!!"); }); <div class="se-preview-section-delimiter"></div>
2.dispatch_once
//使用dispatch_once实现单例,以及和互斥锁实现单例的比较
(dispatch_once实现的单例比互斥锁实现的单例效率高)
// dispatch_once {}// 1.要提供一个全局的访问点// 2.在这个项目中,只会有一个实例对象+(instancetype)sharedHttpTool { static id instance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // 在这里写的代码在整个项目运行过程中只会执行一次 NSLog(@"是一次吗"); instance = [[self alloc] init]; }); return instance;}// 使用互斥锁实现单例+ (instancetype)sharedSync { static id instance = nil; @synchronized(self) { if (instance == nil) { instance = [[self alloc] init]; } } return instance;}<div class="se-preview-section-delimiter"></div>
8.调度组
- (void)gcdDemo1 { // 组对象 dispatch_group_t group = dispatch_group_create(); // 队列 dispatch_queue_t queue = dispatch_get_global_queue(0, 0); dispatch_group_async(group, queue, ^{ [NSThread sleepForTimeInterval:0.5]; NSLog(@"下载图片1=%@",[NSThread currentThread]); }); dispatch_group_async(group, queue, ^{ [NSThread sleepForTimeInterval:1.5]; NSLog(@"下载图片2=%@",[NSThread currentThread]); }); dispatch_group_async(group, queue, ^{ [NSThread sleepForTimeInterval:2.5]; NSLog(@"下载图片3=%@",[NSThread currentThread]); }); // dispatch_group_notify:当组里面的所有任务执行完毕,会在主队列中添加任务.并执行任务 dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"更新UI %@",[NSThread currentThread]); }); NSLog(@"come here");// dispatch_group_async(group, dispatch_get_main_queue(), ^{// NSLog(@"更新UI %@",[NSThread currentThread]);;// });}<div class="se-preview-section-delimiter"></div>
9.dispatch_apple的使用
NSArray *arr = @[@1,@2,@3,@4,@5]; CFTimeInterval begin1 = CFAbsoluteTimeGetCurrent(); dispatch_apply(arr.count, dispatch_get_global_queue(0, 0), ^(size_t index) { NSLog(@"%@,%@",arr[index],[NSThread currentThread]); }); CFTimeInterval begin2 = CFAbsoluteTimeGetCurrent(); NSLog(@"%lf",begin1 - begin2);<div class="se-preview-section-delimiter"></div>
- 4.Objective-C的NSOperation和NSOperationQueue(基于GCD)
###示例代码:
1.用start添加任务和开启线程
//添加任务和线程的开启- (void)opDemo1{ NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test) object:nil]; //用start开启开启线程是在当前线程中执行任务的 [op start];}- (void)test{ NSLog(@"it is test method-->%@",[NSThread currentThread]);}<div class="se-preview-section-delimiter"></div>
2.operation添加操作和开启线程
- (void)opDemo2{ NSOperationQueue *queue = [[NSOperationQueue alloc] init]; //设置队列的并发数 //如果设置为1,那么就变成了串行队列 queue.maxConcurrentOperationCount = 2; for (int i = 0; i < 10; i ++) { //添加操作 NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(test:) object:nil]; //往队列中添加操作就-->开启了异步(除了在mainQueue中不开启子线程) [queue addOperation:op]; } NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"这是block操作-->%@",[NSThread currentThread] ); }]; [[[NSOperationQueue alloc] init] addOperation:op];}<div class="se-preview-section-delimiter"></div>
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];
3.线程间通信
//线程间的通讯- (void)opDemo3{ [[[NSOperationQueue alloc] init] addOperationWithBlock:^{ [NSThread sleepForTimeInterval:2.0]; NSLog(@"这是耗时操作--%@",[NSThread currentThread]); [[NSOperationQueue mainQueue] addOperationWithBlock:^{ NSLog(@"UI更新--%@",[NSThread currentThread]); }]; }];}
4.队列的挂起,开启以及取消队列操作
//控制队列的挂起和开启的按钮点击事件- (IBAction)pauseAndResume { if(self.queue.operationCount == 0) { NSLog(@"队列中没有操作"); return; } // 设置队列的挂起状态 // setter = !getter self.queue.suspended = !self.queue.isSuspended; // 挂起队列不会影响正在执行的操作 // operationCount中包含没有执行完毕的所有操作. // 队列如果是挂起的,再往队列中添加操作,也不会执行 if (self.queue.suspended) { NSLog(@"暂停 = %zd",self.queue.operationCount); } else { NSLog(@"继续 = %zd",self.queue.operationCount); }}- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { for (int i = 0; i < 20; ++i) { NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ [NSThread sleepForTimeInterval:1.0]; NSLog(@"%@--%d", [NSThread currentThread],i); }]; [self.queue addOperation:op]; }}- (NSOperationQueue *)queue { if (_queue == nil) { _queue = [[NSOperationQueue alloc] init]; // 设置最大并发数 _queue.maxConcurrentOperationCount = 2; } return _queue;}
5.任务之间添加依赖
- (void)op1{ NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSBlockOperation *bop = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"111bop,%@",[NSThread currentThread]); }]; //往bop任务中添加额外的任务 [bop addExecutionBlock:^{ NSLog(@"2222---%@",[NSThread currentThread]); }]; NSInvocationOperation *iop = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(top) object:nil]; NSInvocationOperation *iop2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(top2) object:nil]; //必须先添加依赖,再把任务添加到队列中 [bop addDependency:iop]; [iop addDependency:iop2]; [queue addOperation:bop]; //添加任务到队列中 [queue addOperation:iop]; [queue addOperation:iop2]; }
0 0
- 多线程以及底层实现
- 多线程底层的实现
- 多线程的底层实现
- hashmap的底层以及实现
- ++i和i++ 以及其底层实现
- strcpy函数用法以及底层实现
- 数据库连接池底层原理以及手写实现
- handler的原理以及底层的实现
- java多线程编程底层原理剖析以及volatile原理
- iOS 多线程编程<十三、NSOperation图片下载,SDWebImage底层实现原理>
- iOS与OS多线程和内存管理----GCD底层实现
- 图解Arraylist内存分配,以及底层实现,扩容性能分析
- ConcurrentHashMap源码阅读以及底层实现的简单分析
- HashMap和TreeMap区别详解以及底层实现
- hash冲突的解决方法以及hashMap的底层实现
- HashMap底层实现原理,以及和Hashtable的比较
- Android-GreenDao增删改查以及底层实现简介
- ArryList和LinkedList的底层实现以及比较
- XML与DTD
- 第十四周上机实践项目1(2):阅读程序
- FIREFOX 下载中文文件名出现乱码的java解决方案
- 好吧,第二篇
- 立体类族共有的抽象类
- 多线程以及底层实现
- 随机生成11位激活码
- 第15周 项目2---洗牌
- PackageInfo-获取权限为空
- 第14周程序阅读
- PowerDesigner 使用常见问题锦集(转载)
- Android控件GridView之仿支付宝钱包首页带有分割线的GridView九宫格的完美实现
- 多线程的同步方法及实现
- 轻应用、Web App、Native App三者分别是什么?