iOS开发笔记-多线程的使用方法

多线程方式一:GCD
队列的3个种类:

  1. 自建队列: 分并行/串行
  2. 全局队列: 属于并行队列, 是系统默认创建的.
  3. 主队列: 主线程上的队列. 串行队列

1.自建队列四种
异步:异于当前方法所在线程 async
同步:同于当前方法所在线程 sync
串行:任务按顺序执行 SERIAL
并行:任务没有顺序,同时执行 CONCURRENT

switch (indexPath.section) {
        case 0: //异步串行
        {
            //队列:任务是放在队列中的
            //SERIAL串行,序列  CONCURRENT 并行
            dispatch_queue_t queue = dispatch_queue_create("串行", DISPATCH_QUEUE_SERIAL);
            //队列放方法,并且规定方法在哪个线程中执行
            //把参数2的方法放到参数1的队列中,异步执行
            dispatch_async(queue, ^{
                [self doMethod0];
            });
            dispatch_async(queue, ^{
                [self doMethod1];
            });
        }
            break;
        case 1: //异步并行
        {
            dispatch_queue_t queue = dispatch_queue_create("并行", DISPATCH_QUEUE_CONCURRENT);
            dispatch_async(queue, ^{
                [self doMethod0];
                //[self doMethod1];这样写,自然就串行了
            });
            dispatch_async(queue, ^{
                [self doMethod1];
            });
        }
            break;
        case 2://同步串行
        {
            [self doMethod0];
            [self doMethod1];
            return;
            dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
            dispatch_sync(queue, ^{
                [self doMethod0];
            });
            dispatch_sync(queue, ^{
                [self doMethod1];
            });
        }
            break;
        case 3: //同步并行
        {
            dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
            dispatch_sync(queue, ^{
                [self doMethod0];
            });
            dispatch_sync(queue, ^{
                [self doMethod1];
            });
        }
            break;
            
        default:
            break;
    }

2.全局队列和主队类

- (void)viewDidLoad {
    [super viewDidLoad];
    //dispatch_get_global_queue获取全局队列, 参数1代表优先级, 0是默认优先级. 参数2 无用
   dispatch_async(dispatch_get_global_queue(0, 0), ^{
       for (int i = 0; i < 10000; i++) {
           NSLog(@"%@, %d", [NSThread currentThread], i);
       }
       //dispatch_get_main_queue() 主队列 嵌套在全局队列中 与上方for循环属于全局队列中的一个任务所以是串行的
       dispatch_async(dispatch_get_main_queue(), ^{
          //所有与UIKit相关的操作一定要放到主线程中
           UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
           v.backgroundColor = [UIColor redColor];
           [self.view addSubview:v];
           v.center = self.view.center;
       });
   });
}

barrier(墙) 和分组

- (void)refreshImage{
    NSArray *imagePaths = @[@"http://imgstore.cdn.sogou.com/app/a/11220002/7291_pc.jpg", @"http://b.zol-img.com.cn/desk/bizhi/image/3/960x600/1376532627798.jpg", @"http://pic27.nipic.com/20130304/11627225_191300344144_2.jpg"];
    //barrier(墙): 墙必须加到自建队列中.
    dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
    [imagePaths enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSURL *url = [NSURL URLWithString:obj];
        dispatch_async(queue, ^{
            NSData *data = [NSData dataWithContentsOfURL:url];
            UIImage *img = [UIImage imageWithData:data];
            dispatch_async(dispatch_get_main_queue(), ^{
                self.imageViews[idx].image = img;
            });
        });
    }];
    //在自建的并行队列中添加墙, 墙只有在并行队列中的所有任务都执行结束后, 才会被推倒
    dispatch_barrier_async(queue, ^{
        NSLog(@"%@", [NSThread currentThread]);
        [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
    });
    
    return;
    //可以把多个子线程放到一个分组中, 通过监听此分组的情况来决定一些操作
    dispatch_group_t group = dispatch_group_create();
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
    [imagePaths enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSURL *url = [NSURL URLWithString:obj];
        dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
            NSData *data = [NSData dataWithContentsOfURL:url];
            UIImage *image = [UIImage imageWithData:data];
            dispatch_async(dispatch_get_main_queue(), ^{
                self.imageViews[idx].image = image;
            });
        });
    }];
    
    //监听分组中的线程都执行结束的时机
    dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
        NSLog(@"%@", [NSThread currentThread]);
        [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
    });
    
    
}

死锁

- (void)viewDidLoad {
    [super viewDidLoad];
    /*
     死锁: 在`主线程` 中 调用 `dispatch_sync` 同步方法
     向 `主线程` 中放入一个任务
     
     dispatch_sync() will not return until the block has finished.
     dispatch_sync()这个方法直到block完成以后,才返回.
     
     下方的代码是 向主线程中插入一个 block. 根据官方要求 必须是这个block执行结束以后,dispatch_sync()方法才能执行完毕.
     线程特点: 一个线程同时只能做一件事情, 多个任务是排队执行的.
     看主线程队列: dispatch_sync()方法 -> 打印block方法
     dispatch_sync()的完成需要block方法执行完毕.
     因为排队问题 block 必须在 dispatch_sync() 执行完毕后再进行!!!
     */
   
    /*
     面试问题:下方方法是否有什么问题?
     答: 有可能造成死锁. 因为取决于这个方法执行的线程, 只有在主线程才会死锁.. 说下死锁的三个关键点.
     */
    
    //dispatch_async(dispatch_get_global_queue(0, 0), ^{
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@".....");
        });
    //});
    
    //如果解决这个死锁问题?
    //dispatch_async()方法不要内部的block完成就可以完成
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@".....");
    });
    
    self.view.backgroundColor = [UIColor yellowColor];
    
    
}

多线程方式二:NSThread

- (void)redBtnClicked:sender{
    NSLog(@"redBtnClicked %@", [NSThread currentThread]);
    //在后台线程执行方法
    //[self performSelectorInBackground:@selector(wasteTime) withObject:nil];
    
    //方式2:
    //[NSThread detachNewThreadSelector:@selector(wasteTime) toTarget:self withObject:nil];
    
    //方式3:
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(wasteTime) object:nil];
    thread.name = @"1606";
    [thread start];//必须手动开启
    
}
//开启一个新的线程, 在线程中做耗时操作
- (void)wasteTime{
    //[NSThread currentThread]: 获取当前方法所在的线程
    //number=1 代表主线程, 其他都是子线程
    for (int i = 0; i < 10000; i++) {
        NSLog(@"%d , %@", i, [NSThread currentThread]);
    }

    //从子线程回归主线程,规避掉线程不安全的操作
    [self performSelectorOnMainThread:@selector(changeUI) withObject:nil waitUntilDone:NO];
    //waitUntilDone:表示是否要等待changeUI这个方法执行结束以后, 才继续执行下方代码
    NSLog(@"--------------------");
}

多线程方式三:NSOperation
NSOperation 就是 使用OC语法 对 GCD做的封装, 效率上略低(可以忽略不计), 继续如何选择 看个人喜好
NSOperationQueue : 队列
NSOperation : 任务
任务 存放在 队列 中, 队列 存放在 线程

//NSOperation 封装了GCD之后, 额外增加的易用方法
- (void)runMethod1{
    NSOperationQueue *queue = [NSOperationQueue new];
    
    NSBlockOperation *op0 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op0 开始, %@", [NSThread currentThread]);
        sleep(2);
        NSLog(@"op0 结束, %@", [NSThread currentThread]);
    }];
    
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op1 开始, %@", [NSThread currentThread]);
        sleep(2);
        NSLog(@"op1 结束, %@", [NSThread currentThread]);
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op2 开始, %@", [NSThread currentThread]);
        sleep(2);
        NSLog(@"op2 结束, %@", [NSThread currentThread]);
    }];
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op3 开始, %@", [NSThread currentThread]);
        sleep(2);
        NSLog(@"op3 结束, %@", [NSThread currentThread]);
    }];
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op4 开始, %@", [NSThread currentThread]);
        sleep(2);
        NSLog(@"op4 结束, %@", [NSThread currentThread]);
    }];
    NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op5 开始, %@", [NSThread currentThread]);
        sleep(2);
        NSLog(@"op5 结束, %@", [NSThread currentThread]);
    }];
    NSBlockOperation *op6 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op6 开始, %@", [NSThread currentThread]);
        sleep(2);
        NSLog(@"op6 结束, %@", [NSThread currentThread]);
    }];
    NSBlockOperation *op7 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op7 开始, %@", [NSThread currentThread]);
        sleep(2);
        NSLog(@"op7 结束, %@", [NSThread currentThread]);
    }];
    NSBlockOperation *op8 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op8 开始, %@", [NSThread currentThread]);
        sleep(2);
        NSLog(@"op8 结束, %@", [NSThread currentThread]);
    }];
    NSBlockOperation *op9 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op9 开始, %@", [NSThread currentThread]);
        sleep(2);
        NSLog(@"op9 结束, %@", [NSThread currentThread]);
    }];
    //以为单线程CPU(硬件), 开启多线程, 是伪多线程. 就是把cpu的线程分很多个时间片, 每个时间片做一件事. 因为速度太快了, 所以显得跟多线程一样.
    //最佳的线程数量是3~5个
    
    //NSOperatin就提供了最大并发数量的属性, 此属性必须在任务添加到队列`之前`写才有用!!
    queue.maxConcurrentOperationCount = 3;
    
    //规定线程依赖. 比如a必须等b线程做完才能进行
    //op0 必须等 op7 和 op9 执行完毕 才能进行
    [op0 addDependency:op7];
    [op0 addDependency:op9];
    
    //任务添加到队列中之后, 任务就会自动执行
    //[queue addOperation:op0];
    //waitUntilFinished:参数表示,下方的打印方法是否要等待队列中的任务执行结束之后, 再运行
    [queue addOperations:@[op0, op1, op2, op3, op4, op5, op6, op7, op8, op9] waitUntilFinished:NO];
    NSLog(@"!@#$##$^^&^&$^@#$");
    
    
    //取消线程中的所有任务: 已经执行的任务无法取消
    //[queue cancelAllOperations];
    
    //取消个别任务
    [op2 cancel]; //已经执行的取消不了
    [op9 cancel]; //0依赖9, 9被取消, 0就不再依赖9
    
    //暂停, 已经运行的任务 无法暂停
    queue.suspended = YES;
    sleep(10);
    queue.suspended = NO;
}

//开子线程+回归主线程
- (void)runMethod0{
    //[NSOperationQueue new] 自动开启子线程
    [[NSOperationQueue new] addOperationWithBlock:^{
        NSLog(@"[NSOperationQueue new], %@", [NSThread currentThread]);
        //回归主线程
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
           NSLog(@"[NSOperationQueue mainQueue], %@", [NSThread currentThread]);
        }];
    }];
}

你可能感兴趣的:(iOS开发笔记-多线程的使用方法)