46.多线程

1.多线程的基本概念

  1. 程序:由源代码生成的可执行应用(QQ.app),进程:一个正在运行的程序可以看做一个进程,进程拥有独立运行所需要的全部资源,线程:程序中独立运行的代码段(接收QQ消息的代码)
  2. 每个正在运行的程序(即进程)至少有一个线程,进程只负责资源的调度和分配,线程才是程序真正的执行单元,负责代码的执行
  3. 只有一个主线程的程序叫单线程程序,主线程负责执行程序的所有代码(UI展现和数据刷新,网络请求,本地存储等),这些代码只能顺序执行,不能并发执行
  4. iOS允许用户自己开辟新的线程,子线程和主线程都是独立运行的单元,各自的执行互不影响,所以能够并发执行
  5. 单线程程序:只有一个线程,代码顺序执行,容易出现代码阻塞 多线程程序:有多个线程,线程间独立运行,能有效的避免代码阻塞,并且提高程序的运行性能
  6. iOS中关于UI的添加和刷新必须在主线程中操作,应用程序打开的时候,系统会自动为主线程创建一个自动释放池,我们手动创建的子线程需要我们手动添加自动释放池
  7. 线程间通信分两种:
    1) 主线程进入子线程(下面的四种方法都可以)
    2)子线程回到主线程:GCD用>>>dispatch_get_main_queue() NSObject>>>用- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
  8. 线程互斥是指某一资源同时只能允许一个访问者进行访问,具有唯一性和排他性,互斥无法限制访问者对资源的访问顺序,因此需要加上互斥锁来进行顺序访问(代表:买票系统),NSLock类能协助完成互斥操作
  9. GCD介绍
    1) GCG是苹果公司提供的一种处理多线程的解决方案,性能更高,功能更强大
    2) 任务:具有一定功能的代码块段,一般是一个block或者函数
    3) 分发队列:GCD以队列的方式进行工作,FIFO
    4) GCD会根据分发队列的类型,创建合适数量的线程执行队列中的任务
    5) GCD中两种队列(dispatch queue): (1)SerialQueue能实现线程同步,一次只执行一个任务,通常用于同步访问特定的资源或数据,但当我们创建Serial queue时,虽然他们各自是同步执行的,但是Serial queue与Serial queue之间是并发执行的 (2)Concurrent:可以并发地执行多个任务,但是遵守FIFO
  10. GCD功能
    1) dispatch_async():往队列中添加任务,任务会排队执行
    2) dispatch_after():在延迟的时间点排队执行
    3) dispatch_apply():任务会重复执行n次
    4) dispatch_group_async():添加分组标记
    5) dispatch_group_notify():当某个分组的所有任务执行完之后,此任务才会执行
    6) dispatch_barrier_async():此任务执行的时候,其它任务停止执行
    7) dispatch_once():任务在程序运行过程中只执行一次
    8) dispatch_async_f():任务是函数非block
    9) dispatch_sync():block代码不执行完,下面的代码不会执行
  11. iOS多线程实现的四种: 1)NSObject 2)NSThread 3)NSOperationQueue 4)GCD >>线程尽管提升了性能,但是存在一些访问限制,如线程同步和线程互斥等

2.四种多线程的简单使用

#import "ViewController.h"
#import "MyOperation.h"
#import "MBProgressHUD.h"
#import "AFHTTPRequestOperation.h"
@interface ViewController ()
@property(nonatomic,retain)UIButton *button;
@property(nonatomic,retain)UIImageView *myImageView;
@property(nonatomic,retain)MBProgressHUD *hud;
@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.button = [UIButton buttonWithType:UIButtonTypeCustom];
    self.button.frame = CGRectMake(100, 180, 175, 40);
    self.button.backgroundColor = [UIColor cyanColor];
    [self.button setTitle:@"测试" forState:UIControlStateNormal];
    [self.button setTitle:@"正在测试" forState:UIControlStateHighlighted];
    [self.view addSubview:self.button];
    //添加一个点击事件,用来调用后面的方法
    [self.button addTarget:self action:@selector(downloadNumPdf:)  forControlEvents:UIControlEventTouchUpInside];

    self.myImageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 250, 175, 175)];
    //NSURL *url = [NSURL URLWithString:@"http://img4.duitang.com/uploads/item/201207/28/20120728105310_jvAjW.thumb.600_0.jpeg"];
    //NSData *data = [NSData dataWithContentsOfURL:url];
    //self.myImageView.image = [UIImage imageWithData:data];
    [self.view addSubview:self.myImageView];
}
//计算1--1亿的和
- (void)click:(UIButton *)button{
    NSInteger count = 0;
    for (NSInteger i = 0; i < 100000000; i++) {
        count++;
        NSLog(@"%ld",count);
    }
}

第一种:NSObject实现异步后台执行(某个方法)

- (void)NSObjectThread:(UIButton *)button{
    [self performSelectorInBackground:@selector(click:) withObject:button];
    //优点:写法特别简单,能快速开辟一个临时的线程  缺点:不能保证线程使用时的数据安全
}

第二种:NSThread轻量级的多线程

-(void)NSThreadAction:(UIButton *)button{
    //NSThread本来就是一个线程类,它可以控制线程休眠或创建
    NSLog(@"%@",[NSThread currentThread]);//当前主线程
    //主线程休眠3秒
    [NSThread sleepForTimeInterval:3];
    //如果用NSThread创建对象,创建出来的就是新的线程(两种方法创建)
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(click:) object:nil];
    //[NSThread detachNewThreadSelector:@selector(click:) toTarget:self withObject:nil];
    thread.name = @"myThread";//可以给新的线程对象起名
    [thread start];//开启子线程--->[thread cancel];
    //优点:可以接通创建的方式来控制线程 缺点:什么都需要手动设置,包括名和开始
}

第三种:NSOperation和NSOperationQueue
写一个NSOperation的子类,MyOperation文件

#import <Foundation/Foundation.h>
@interface MyOperation : NSOperation
@end

#import "MyOperation.h"
@implementation MyOperation
//重写父类的main方法
- (void)main{
    NSInteger count = 1;
    for (NSInteger i = 0; i < 6000000; i++) {
        count++;
    }
    NSLog(@"%ld",count);
}
@end
- (void)operationAction:(UIButton *)button{
    //NSOperation类在MVC中属于M,是用来封装单个任务相关的代码和数据的抽象类,所以我们使用它的子类(NSInvocationOperation或NSBlockOperation)来执行实际任务,NSInvocationOperation封装了执行操作的target和要执行的action,NSBlockOperation封装了要执行的代码块
    MyOperation *operatin = [[MyOperation alloc] init];
    [operatin start];
    //NSOperation(含子类)只是一个操作,本身无主线程和子线程之分,可以在任意线程中使用,所以它不能单独拿来用,否则和之前不考虑线程的方式是一样会卡死主线程,它一般和NSOperationQueue配合使用
}
- (void)operationQueue:(UIButton *)button{
    //队列和任务一起配合使用解决多线程问题
    //队列:队列中通过一个线程池来管理所有闲置的线程,这样可以提高线程的重用利用率,避免重复创建线程,整合资源
    //优点:内部不需要关心线程的安全问题,用起来相对简单 缺点:效率稍微有点低
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    //设置最大并发数,当设置为1时,能实现线程同步
    [queue setMaxConcurrentOperationCount:2];
    //先创建五个任务,然后把添加到队列里
    MyOperation *op1 = [[MyOperation alloc] init];
    MyOperation *op2 = [[MyOperation alloc] init];
    MyOperation *op3 = [[MyOperation alloc] init];
    MyOperation *op4 = [[MyOperation alloc] init];
    MyOperation *op5 = [[MyOperation alloc] init];
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
    [queue addOperation:op4];
    [queue addOperation:op5];
    //NSOperationQueue是操作队列,它用来管理一组Operation对象的执行,会根据需要自动为Operation开辟合适数量的线程,以完成任务的并发执行,Nsoperation可以调节它在队列中的优先级
}

第四种:GCD(Grand Central Dispatch)函数级的多线程

- (void)GCDAction:(UIButton *)button{
    //这个方法可以保证无论哪个线程执行都只执行一次
    static dispatch_once_t oneToken;
    dispatch_once(&oneToken, ^{

    });
#if 0//自定义一个队列,两个参数:1.给队列起名 2.设置队列并行
    dispatch_queue_t myQueue = dispatch_queue_create("myQueue",DISPATCH_QUEUE_CONCURRENT);
    //dispatch_async():往队列中添加任务,任务会排队执行
    dispatch_async(myQueue, ^{
        //任务写在block里面
        NSInteger count = 0;
        for (NSInteger i = 0; i < 60000; i++) {
            count++;
            NSLog(@"%ld",count);
        }
    });
#endif
    //网络请求在子线程里进行加载,显示数据在主线程里进行
    //定义一个全局的队列,两个参数:1.设置当前队列优先 2.没有实际含义,留给以后用
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //在获取一下当前的主队列,也就是主线程
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_async(globalQueue, ^{
        NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://img4.duitang.com/uploads/item/201207/28/20120728105310_jvAjW.thumb.600_0.jpeg"]];
        NSData *data = [NSData dataWithContentsOfURL:url];
        UIImage *image = [UIImage imageWithData:data];
        //数据请求完毕后回到主线程加载数据
        dispatch_async(mainQueue, ^{
            self.myImageView.image = image;
        });
    });
#if 0//上面的请求数据和下面的方法差不多
    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://img4.duitang.com/uploads/item/201207/28/20120728105310_jvAjW.thumb.600_0.jpeg"]];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
        // data是从网络返回的数据,对data处理后重新回到主线程工作
        dispatch_async(dispatch_get_main_queue(), ^{
            UIImage *image = [UIImage imageWithData:data];
            self.myImageView.image = image;// 更新UI
        });
    }];

#endif
}

调用下面这个方法,使用AFN通过多线程下载一个pdf文档,并显示下载进度

- (void)downloadNumPdf:(UIButton *)button{
    //创建一个加载的效果图MB
    self.hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
    //设置下载样式
    self.hud.mode = MBProgressHUDModeDeterminate;
    //创建一个请求
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://help.adobe.com/archive/en/photoshop/cs6/photoshop_reference.pdf"]];
    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    //指定一个文件保存路径,放到沙盒的caches文件中
    NSArray *sandBox = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    NSString *paht = [sandBox[0] stringByAppendingPathComponent:@"test.pdf"];
    //把文件下载到指定文件夹路径下,写成相应文件名
    operation.outputStream = [NSOutputStream outputStreamToFileAtPath:paht append:NO];
    //通过AF下载,这是一个下载进度的block,里面会返回当前的下载进度,在这个block里设置hud的进度显示
    [operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
        self.hud.progress = (1.0)*(totalBytesRead)/totalBytesExpectedToRead;
    }];
    //当下载结束之后,控制进度的hud应该消失,所以
    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
        [self.hud removeFromSuperview];
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        [self.hud removeFromSuperview];
        NSLog(@"%@",error);
    }];
    //把任务添加到队列里面(第三种多线程)
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation:operation];
    //添加一个定时器定时更新self.hud的上标题的下载进度
    [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(downloadNum:) userInfo:nil repeats:YES];
}
- (void)downloadNum:(NSTimer *)timer{
    NSInteger loadPercent = self.hud.progress*100;//progress(0.0--1.0)
    self.hud.labelText = [NSString stringWithFormat:@"正在下载:%ld%%",loadPercent];
    if (self.hud.progress == 1.0) {
        self.hud.labelText = @"下载成功";
        [timer invalidate];
    }
}
@end

下载效果截图
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值