多线程

2017-01-12  本文已影响26人  CoderLNHui

# 多线程使用

一、NSThread

1、创建的3种基本使用方式

//第一种创建线程的方式:alloc init.

//特点:需要手动开启线程,可以拿到线程对象进行详细设置

//创建线程

/*

 第一个参数:目标对象

 第二个参数:选择器,线程启动要调用哪个方法

 第三个参数:前面方法要接收的参数(最多只能接收一个参数,没有则传nil)

 */

NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"person"];

//启动线程

[thread start];
//设置属性

thread.name = @"线程";

 //设置优先级  取值范围 0.0 ~ 1.0 之间 最高是1.0 默认优先级是0.5

thread.threadPriority = 1.0;

//第二种创建线程的方式:分离出一条子线程

//特点:自动启动线程,无法对线程进行更详细的设置

/*

 第一个参数:线程启动调用的方法

 第二个参数:目标对象

 第三个参数:传递给调用方法的参数

 */

[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"我是分离出来的子线程"];

//第三种创建线程的方式:后台线程

//特点:自动启动线程,无法进行更详细设置

[self performSelectorInBackground:@selector(run:) withObject:@"后台线程"];

2、线程之间通信方式


- (void)viewDidLoad

{    //开启一条子线程来下载图片

    [NSThread detachNewThreadSelector:@selector(downloadImage) toTarget:self withObject:nil];

}
-(void)downloadImage

{

    //1.确定要下载网络图片的url地址,一个url唯一对应着网络上的一个资源

    NSURL *url = [NSURL URLWithString:@"下载图片的urlStr"];

    

    //2.根据url地址下载图片数据到本地(二进制数据

    NSData *data = [NSData dataWithContentsOfURL:url];

    

    //3.把下载到本地的二进制数据转换成图片

    UIImage *image = [UIImage imageWithData:data];

    

    //4.回到主线程刷新UI

    //4.1 第一种方式

    //    [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];

    

    //4.2 第二种方式

    //    [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];

    

    //4.3 第三种方式

    [self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];

}

二、GCD

1、核心概念:任务(执行什么操作)和队列(用来存放任务)

任务

同步函数dispatch_sync

queue:队列

block:任务

dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
异步函数dispatch_async
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

同步和异步主要影响:能不能开启新的线程

队列

并发队列(只有在异步(dispatch_async)函数下才有效)
并发队列创建方式
dispatch_queue_t

dispatch_queue_create(const char *label, // 队列名称

                      dispatch_queue_attr_t attr); // 队列的类型

/*

     第一个参数:C语言的字符串,标签

     第二个参数:队列的类型

        DISPATCH_QUEUE_CONCURRENT:并发

        DISPATCH_QUEUE_SERIAL:串行

  */

    dispatch_queue_t queue = dispatch_queue_create("biaoqian", DISPATCH_QUEUE_CONCURRENT);
获得全局并发队列dispatch_get_global_queue

/*

     第一个参数:队列优先级

#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// 后台

     第二个参数:此参数暂时无用,用0即可

     */

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

串行队列
 - 让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
获得串行队列方式

// 创建串行队列(队列类型传递NULL或者DISPATCH_QUEUE_SERIAL)

dispatch_queue_t queue = dispatch_queue_create("com.queue", NULL);
主队列是GCD自带的一种特殊的串行队列

放在主队列中的任务,都会放到主线程中执行

dispatch_queue_t queue = dispatch_get_main_queue();

并发和串行主要影响:任务的执行方式

2、任务和队列搭配使用

3、GCD线程间的通信

dispatch_async(dispatch_get_main_queue(), ^{}

//1.创建子线程下载图片

//DISPATCH_QUEUE_PRIORITY_DEFAULT 0

dispatch_async(dispatch_get_global_queue(0, 0), ^{

    

    //1.1 确定url

    NSURL *url = [NSURL URLWithString:@"UrlStr"];

    

    //1.2 下载二进制数据到本地

    NSData *imageData =  [NSData dataWithContentsOfURL:url];

    

    //1.3 转换图片

    UIImage *image = [UIImage imageWithData:imageData];
    

    //更新UI,dispatch_sync(dispatch_get_main_queue(),并不会造成死锁,在子线程中调用

    // dispatch_async(dispatch_get_main_queue(), ^{

    dispatch_sync(dispatch_get_main_queue(), ^{

        self.imageView.image = image;

    });

    

});

4、GCD其他常用函数

a、延迟执行三种方式

performSelector

1. 延迟执行的第一种方法

[self performSelector:@selector(task) withObject:nil afterDelay:2.0];
NSTimer scheduledTime...

2.延迟执行的第二种方法

[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(task) userInfo:nil repeats:YES];
dispatch_after

3.GCD

//    dispatch_queue_t queue = dispatch_get_main_queue();

 dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

/*

 第一个参数:DISPATCH_TIME_NOW 从现在开始计算时间

 第二个参数:延迟的时间 2.0 GCD时间单位:纳秒

 第三个参数:队列

 */

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), queue, ^{

    NSLog(@"GCD----%@",[NSThread currentThread]);

});

b、一次性代码(不能放在懒加载中的,应用场景:单例模式)

dispatch_once

-(void)once

{

    //整个程序运行过程中只会执行一次

    //onceToken用来记录该部分的代码是否被执行过

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        NSLog(@"---once----");

    });

}

c、快速迭代(开子线程和主线程一起完成遍历任务,任务执行时是并发的)

dispatch_apply

    /*

     第一个参数:遍历的次数

     第二个参数:队列(并发队列)

     第三个参数:index 索引

     */

    dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) {

        NSLog(@"%zd---%@",index,[NSThread currentThread]);

    });
#### d、栅栏函数(不能使用全局并发队列)
##### dispatch_barrier_async

dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_CONCURRENT);
//栅栏函数

dispatch_barrier_async(queue, ^{

     NSLog(@"+++++++++++++++++++++++++++++");

 });

e、队列组

dispatch_group_create
dispatch_group_async
dispatch_group_notify

主要函数:

//1.创建队列

dispatch_queue_t queue =dispatch_get_global_queue(0, 0);

//2.创建队列组

dispatch_group_t group = dispatch_group_create();

//3.异步函数

/*

 1)封装任务

 2)把任务添加到队列中

 dispatch_async(queue, ^{

 NSLog(@"1----%@",[NSThread currentThread]);

 });

 */

/*

 1)封装任务

 2)把任务添加到队列中

 3)会监听任务的执行情况,通知group

 */

dispatch_group_async(group, queue, ^{

});

//拦截通知,当队列组中所有的任务都执行完毕的时候回进入到下面的方法

dispatch_group_notify(group, queue, ^{ 

});

应用举例

-(void)group

{

    /*

     1.下载图片1 开子线程

     2.下载图片2 开子线程

     3.合成图片并显示图片 开子线程

     */

    //-1.获得队列组

    dispatch_group_t group = dispatch_group_create();

    //0.获得并发队列

    dispatch_queue_t queue =  dispatch_get_global_queue(0, 0);

    // 1.下载图片1 开子线程

    dispatch_group_async(group, queue,^{

        NSURL *url = [NSURL URLWithString:@"urlStrImage1"];

        NSData *imageData = [NSData dataWithContentsOfURL:url];

        self.image1 = [UIImage imageWithData:imageData];

    });    

    // 2.下载图片2 开子线程

     dispatch_group_async(group, queue,^{
         //2.1 确定url

        NSURL *url = [NSURL URLWithString:@"urlStrImage2"];

        

        //2.2 下载二进制数据

        NSData *imageData = [NSData dataWithContentsOfURL:url];

        

        //2.3 转换图片

        self.image2 = [UIImage imageWithData:imageData];

    });

    //3.合并图片

    //dispatch_group_notify内部本身是异步的,不会阻塞

    dispatch_group_notify(group,queue, ^{

        //3.1 创建图形上下文

        UIGraphicsBeginImageContext(CGSizeMake(200, 200));

     

        //3.2 画图1

        [self.image1 drawInRect:CGRectMake(0, 0, 200, 100)];

        self.image1 = nil;

        

        //3.3 画图2

        [self.image2 drawInRect:CGRectMake(0, 100, 200, 100)];

        self.image2 = nil;

        

        //3.4 根据上下文得到一张图片

        UIImage *image =  UIGraphicsGetImageFromCurrentImageContext();

        

        //3.5 关闭上下文

        UIGraphicsEndImageContext();

        

        //3.6 更新UI

        dispatch_async(dispatch_get_main_queue(), ^{

        

            NSLog(@"UI----%@",[NSThread currentThread]);

            self.imageView.image = image;

        });

    });
}
-(void)group2

{

    //1.创建队列

    dispatch_queue_t queue =dispatch_get_global_queue(0, 0);

    //2.创建队列组

    dispatch_group_t group = dispatch_group_create();

    //3.在该方法后面的异步任务会被纳入到队列组的监听范围,进入群组

    //dispatch_group_enter|dispatch_group_leave 必须要配对使用

    dispatch_group_enter(group);

    

    dispatch_async(queue, ^{

         //离开群组

        dispatch_group_leave(group);

    });

    

    dispatch_group_enter(group);

    

    dispatch_async(queue, ^{

        //离开群组

        dispatch_group_leave(group);

    });

    //拦截通知

    //问题?该方法是阻塞的吗?  内部本身是异步的

//    dispatch_group_notify(group, queue, ^{

//        NSLog(@"-------dispatch_group_notify-------");

//    });

    

    //等待.死等. 直到队列组中所有的任务都执行完毕之后才能执行

    //阻塞的

    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);    

}

三、NSOperation

1、NSOperation和NSOperationQueue实现多线程的具体步骤

- 先将需要执行的操作封装到一个NSOperation对象中

- 然后将NSOperation对象添加到NSOperationQueue中

- 系统会自动将NSOperationQueue中的NSOperation取出来

- 将取出的NSOperation封装的操作放到一条新线程中执行

2、NSOperation子类的3种方式

a、NSInvocationOperation
//1.创建操作,封装任务

/*

 第一个参数:目标对象 self

 第二个参数:调用方法的名称

 第三个参数:前面方法需要接受的参数 nil

 */

 NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(download1) object:nil];
//2.启动|执行操作

 [op1 start];// 默认是同步执行的
b、NSInvocationOperation

1.创建操作

    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{

     }];

/**

 追加任务

 注意:如果一个操作中的任务数量大于1,那么会开子线程并发执行任务

 注意:不一定是子线程,有可能是主线程

 */

[op1 addExecutionBlock:^{

}];

2.启动

[op1 start];
c、自定义NSOption
-(void)main

{

    NSLog(@"--main--%@",[NSThread currentThread]);

}

//如何使用?

//1.实例化一个自定义操作对象

SHOperation *op = [[SHOperation alloc]init];

//2.执行操作

[op start];

3、NSOperationQueue 队列基本使用

NSOperation中的两种队列

//1.创建操作
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{

     NSLog(@"1----%@",[NSThread currentThread]);

}];

//追加任务

[op1 addExecutionBlock:^{

      NSLog(@"4----%@",[NSThread currentThread]);

 }];

//2.创建队列

NSOperationQueue *queue = [[NSOperationQueue alloc]init];

//3.添加操作到队列中,系统会自动异步的执行NSOperation中的操作

[queue addOperation:op1];   //内部已经调用了[op1 start]
/*

简便方法

1)创建操作,2)添加操作到队列中
*/

[queue addOperationWithBlock:^{

    NSLog(@"7----%@",[NSThread currentThread]);

}];

4、NSOperation其他用法

a、设置最大并发数
//1.创建队列

//默认是并发队列

NSOperationQueue *queue = [[NSOperationQueue alloc]init];

//2.设置最大并发数量 maxConcurrentOperationCount

//同一时间最多有多少个任务可以执行

//串行执行任务!=只开一条线程 (线程同步)

queue.maxConcurrentOperationCount = 1;

注意点:该属性需要在任务添加到队列中之前进行设置

该属性控制队列是串行执行还是并发执行

maxConcurrentOperationCount >1 那么就是并发队列

maxConcurrentOperationCount == 1 那就是串行队列

maxConcurrentOperationCount == 0 不会执行任务

maxConcurrentOperationCount == -1 特殊意义 最大值 表示不受限制

b、暂停和恢复suspended
if (self.queue.isSuspended) {
    //恢复

    self.queue.suspended = NO;

}else

{
    //暂停
    self.queue.suspended = YES;

}
c、取消
//该方法内部调用了所有操作的cancel方法 
[self.queue cancelAllOperations];
d、操作监听(completionBlock)和依赖(addDependency)
//1.创建队列

NSOperationQueue *queue = [[NSOperationQueue alloc]init];

NSOperationQueue *queue2 = [[NSOperationQueue alloc]init];

//2.封装操作

NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{

    NSLog(@"1---%@",[NSThread currentThread]);

}];

NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{

    NSLog(@"2---%@",[NSThread currentThread]);

}];

//操作监听

op2.completionBlock = ^{

};

//添加操作依赖

//注意点:不能循环依赖

//可以跨队列依赖

[op1 addDependency:op2];
//添加操作到队列

[queue addOperation:op1];
[queue2 addOperation:op2];

5、NSOpration实现线程间通信

//1.开子线程下载图片

//1.1 非主队列

NSOperationQueue *queue = [[NSOperationQueue alloc]init];

//1.2 封装操作

NSBlockOperation *download = [NSBlockOperation blockOperationWithBlock:^{ 

    //3.更新UI

    [[NSOperationQueue mainQueue] addOperationWithBlock:^{

    }]; 

}];

//2.添加操作到队列

[queue addOperation:download];
上一篇 下一篇

猜你喜欢

热点阅读