iOS 多线程(NSThread、GCD、NSOperation)

  ios中得多线程技术主要使用3种:NSThreadNSOperationGCD

  一、NSThread: 最轻量级方法,但是不安全需要手动加锁,需要自己管理生命周期

     NSThread的使用方法有2种:   

// 第一种,需要start
1
NSThread *th1 = [[NSThread alloc] initWithTarget:self selector:@selector(btnClick) object:nil]; 2 [th1 setName:@"线程1"]; 3 [th1 start];
// 第二种
1
[NSThread detachNewThreadSelector:@selector(btnClick) toTarget:self withObject:nil];

  因为NSThread使用不安全,我们需要给它加锁(主要的实现代码):

 1 @property (nonatomic,assign) NSInteger fruits;
 2 @property (nonatomic,strong) NSLock *lock;
 3 - (void)viewDidLoad
 4 {
 5     [super viewDidLoad];
 6 
 7     _lock = [[NSLock alloc] init];
 8     _fruits = 50;
 9 }
10 
11 - (void)btnClick
12 {
13     @synchronized(_lock){ // 加锁
14         
15         while (_fruits > 0) {
16             
17             NSLog(@"当前线程为:%@ , 剩余票数为:%d",[NSThread currentThread], _fruits);
18             
19             _fruits--;
20         }
21     }
22 }

  二、GCD:(Grand Central Dispatch)是一种多核编码技术,用纯C语言编写。

  异步:具备开启线程的功能

  同步:不具备开启线程的功能

  并行队列:多个任务可以同时执行

  串行队列:执行完一个任务后再执行下一个任务

  下面来进行用代码说明:

 1 // 自定义异步方法
 2 - (void)async:(dispatch_queue_t)queue
 3 {
 4     // 异步执行
 5     dispatch_async(queue, ^{
 6         
 7         NSLog(@"im1-%@",[NSThread currentThread]);
 8     });
 9     dispatch_async(queue, ^{
10         
11         NSLog(@"im2-%@",[NSThread currentThread]);
12     });
13     dispatch_async(queue, ^{
14         
15         NSLog(@"im3-%@",[NSThread currentThread]);
16     });
17     dispatch_async(queue, ^{
18         
19         NSLog(@"im4-%@",[NSThread currentThread]);
20     });
21 
22 }
 1 // 自定义同步方法
 2 - (void)sync:(dispatch_queue_t)queue
 3 {
 4     // 同步执行
 5     dispatch_sync(queue, ^{
 6         
 7         NSLog(@"im1-%@",[NSThread currentThread]);
 8     });
 9     dispatch_sync(queue, ^{
10         
11         NSLog(@"im2-%@",[NSThread currentThread]);
12     });
13     dispatch_sync(queue, ^{
14         
15         NSLog(@"im3-%@",[NSThread currentThread]);
16     });
17     dispatch_sync(queue, ^{
18         
19         NSLog(@"im4-%@",[NSThread currentThread]);
20     });
21 
22 }

调用异步执行方法

1 // 创建一个全局并行队列
2     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
3     
4     // 异步执行
5     [self async:queue];

最终的结果为:开启了四个线程,并且执行顺序是不定

1 2015-04-02 16:29:20.438 GCD[4398:1f07] im3-<NSThread: 0x7177ee0>{name = (null), num = 5}
2 2015-04-02 16:29:20.432 GCD[4398:1303] im1-<NSThread: 0x7177d70>{name = (null), num = 3}
3 2015-04-02 16:29:20.440 GCD[4398:4307] im4-<NSThread: 0x7178490>{name = (null), num = 6}
4 2015-04-02 16:29:20.432 GCD[4398:1a03] im2-<NSThread: 0x75429f0>{name = (null), num = 4}
1 // 创建一个全局串行队列
2     dispatch_queue_t queue = dispatch_queue_create("wys", NULL);
3     
4     // 异步执行
5     [self async:queue];

最终的结果为:开启了一个线程,执行顺序为从上往下依次执行

1 2015-04-02 16:32:19.415 GCD[4442:1303] im1-<NSThread: 0x71631a0>{name = (null), num = 3}
2 2015-04-02 16:32:19.428 GCD[4442:1303] im2-<NSThread: 0x71631a0>{name = (null), num = 3}
3 2015-04-02 16:32:19.437 GCD[4442:1303] im3-<NSThread: 0x71631a0>{name = (null), num = 3}
4 2015-04-02 16:32:19.450 GCD[4442:1303] im4-<NSThread: 0x71631a0>{name = (null), num = 3}

调用主队列执行方法:

1 // 创建主队列
2     dispatch_queue_t queue = dispatch_get_main_queue();
3     
4     // 异步执行
5     [self async:queue];

最红的结果为:顺序执行并且不开启线程,在主线程中执行

1 2015-04-02 16:35:45.320 GCD[4484:c07] im1-<NSThread: 0x71560c0>{name = (null), num = 1}
2 2015-04-02 16:35:45.333 GCD[4484:c07] im2-<NSThread: 0x71560c0>{name = (null), num = 1}
3 2015-04-02 16:35:45.339 GCD[4484:c07] im3-<NSThread: 0x71560c0>{name = (null), num = 1}
4 2015-04-02 16:35:45.347 GCD[4484:c07] im4-<NSThread: 0x71560c0>{name = (null), num = 1}
1 // 创建主队列
2     dispatch_queue_t queue = dispatch_get_main_queue();
3     
4     NSLog(@"start");
5     
6     // 同步执行
7     [self sync:queue];
8     
9     NSLog(@"end");

最终的结果为:执行到start就卡住了,不能往下执行

1 2015-04-02 16:38:12.856 GCD[4514:c07] start

调用同步执行方法:

1 // 创建全局并行队列
2     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
3     
4     // 同步执行
5     [self sync:queue];

最终的结果为:不开启线程,并且顺序执行,直接主线程执行

1 2015-04-02 16:41:27.448 GCD[4555:c07] im1-<NSThread: 0x71133c0>{name = (null), num = 1}
2 2015-04-02 16:41:27.458 GCD[4555:c07] im2-<NSThread: 0x71133c0>{name = (null), num = 1}
3 2015-04-02 16:41:27.468 GCD[4555:c07] im3-<NSThread: 0x71133c0>{name = (null), num = 1}
4 2015-04-02 16:41:27.472 GCD[4555:c07] im4-<NSThread: 0x71133c0>{name = (null), num = 1}
1 // 创建串行队列
2     dispatch_queue_t queue = dispatch_queue_create("wys", NULL);
3     
4     // 同步执行
5     [self sync:queue];

最终的结果为:不开启线程,并且顺序执行,直接主线程执行

1 2015-04-02 16:43:40.609 GCD[4589:c07] im1-<NSThread: 0x713e570>{name = (null), num = 1}
2 2015-04-02 16:43:40.621 GCD[4589:c07] im2-<NSThread: 0x713e570>{name = (null), num = 1}
3 2015-04-02 16:43:40.626 GCD[4589:c07] im3-<NSThread: 0x713e570>{name = (null), num = 1}
4 2015-04-02 16:43:40.634 GCD[4589:c07] im4-<NSThread: 0x713e570>{name = (null), num = 1}

多线程之间的通信:UI界面的更新一定要在主线程中执行

 1 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 全局并行队列异步执行
 2         
 3         
 4         NSLog(@"全局并行队列异步执行 - %@",[NSThread currentThread]);
 5         
 6         // 获取网络图片数据
 7         NSString *str = @"https://ss0.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3384245408,2851245305&fm=21&gp=0.jpg";
 8         NSURL *url = [NSURL URLWithString:str];
 9         
10         NSData *data = [NSData dataWithContentsOfURL:url];
11         
12         UIImage *image = [UIImage imageWithData:data];
13         
14         dispatch_async(dispatch_get_main_queue(), ^{ // 主线程异步执行
15             
16             NSLog(@"主线程异步执行 - %@",[NSThread currentThread]);
17             
18             // 更新界面
19             [_im1 setImage:image];
20         });
21         
22     });

最终的结果为:

2015-04-03 11:37:08.513 GCD[1095:1303] 全局并行队列异步执行 -<NSThread: 0x754c530>{name = (null), num = 3}
2015-04-03 11:37:10.657 GCD[1095:c07] 主线程异步执行 -<NSThread: 0x7132c80>{name = (null), num = 1}

利用多线程来实现延时执行:

1、利用GCD

 1 double delayInSeconds = 2.0; // 延时时长
 2         dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
 3         dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ // 在主线程中执行
 4             
 5             NSLog(@"%@",[NSThread currentThread]);
 6             
 7             // 更新界面
 8             [_im1 setImage:image];
 9             
10         });

2、利用此方法,performSelector为你要调用的方法,withObject为你要传得值,afterDelay为延时时长

1 [self performSelector:@selector(downLoad) withObject:nil afterDelay:0.5f];

队列组:

 1 dispatch_group_t group = dispatch_group_create();
 2     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
 3     
 4     dispatch_group_async(group, queue, ^{
 5         
 6         // 代码1
 7     });
 8     
 9     dispatch_group_async(group, queue, ^{
10         
11         // 代码2
12     });
13     
14     dispatch_group_notify(group, queue, ^{
15         
16         // 执行完代码1和代码2后,再执行此方法
17     });

  

  三、NSOperation:基于GCD,能控制最大并发线程数,主要有2个类:NSInvocationOperation和NSBlockOperation

  其步骤为: 创建队列、添加操作、将操作添加到队列

  A、NSInvocationOperation:

 1 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
 2 {
 3     NSOperationQueue *queue = [[NSOperationQueue alloc] init];
 4     
 5     NSInvocationOperation *invo = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(buy) object:nil];
 6     
 7     
 8     [queue addOperation:invo];
 9     
10 }
11 
12 - (void)buy
13 {
14     NSLog(@"%@",[NSThread currentThread]);
15 }

最后的结果为:开启了一个线程

1 2015-04-03 14:58:54.060 op1[2631:1e07] <NSThread: 0x71a6de0>{name = (null), num = 3}

  

  B、NSBlockOperation

 1 NSOperationQueue *queue = [[NSOperationQueue alloc] init];
 2     
 3     NSBlockOperation *b1 = [NSBlockOperation blockOperationWithBlock:^{
 4         
 5         NSLog(@"111--%@",[NSThread currentThread]);
 6     }];
 7     NSBlockOperation *b2 = [NSBlockOperation blockOperationWithBlock:^{
 8         
 9         NSLog(@"222--%@",[NSThread currentThread]);
10     }];
11     NSBlockOperation *b3 = [NSBlockOperation blockOperationWithBlock:^{
12         
13         NSLog(@"333--%@",[NSThread currentThread]);
14     }];
15     
16     
17     
18     [queue addOperation:b1];
19     [queue addOperation:b2];
20     [queue addOperation:b3];

最后的结果为:开启了三个线程,自动异步执行

1 2015-04-03 15:15:59.897 op1[2798:1a03] 111--<NSThread: 0x7544fa0>{name = (null), num = 3}
2 2015-04-03 15:15:59.901 op1[2798:4307] 222--<NSThread: 0x71506f0>{name = (null), num = 4}
3 2015-04-03 15:15:59.902 op1[2798:1303] 333--<NSThread: 0x7545e60>{name = (null), num = 5}

设置依赖:

1 // 设置依赖 ,b2依赖b1,b3依赖b2,所以执行顺序为,b1->b2->b3
2     [b2 addDependency:b1];
3     [b3 addDependency:b2];

设置队列的最大并发数量

1 // 设置最大并发(最多同时并发执行3个任务)
2     queue.maxConcurrentOperationCount = 2;

  C、主队列

 1 NSOperationQueue *queue = [[NSOperationQueue alloc] init];
 2     
 3     [queue addOperationWithBlock:^{
 4         
 5           NSLog(@"-------%@",[NSThread currentThread]);
 6             
 7                // 获取网络图片数据
 8                  NSString *str = @"https://ss0.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3384245408,2851245305&fm=21&gp=0.jpg";
 9                  NSURL *url = [NSURL URLWithString:str];
10         
11                  NSData *data = [NSData dataWithContentsOfURL:url];
12         
13                  UIImage *image = [UIImage imageWithData:data];
14         
15         [[NSOperationQueue mainQueue] addOperationWithBlock:^{ // 主队列
16                 
17                  NSLog(@"******%@",[NSThread currentThread]);
18                 
19                  // 更新界面
20                  [_im1 setImage:image];
21             }];
22     }];

最后的结果为:

1 2015-04-03 15:27:16.077 op1[2916:1c03] -------<NSThread: 0x71613e0>{name = (null), num = 3}
2 2015-04-03 15:27:17.593 op1[2916:c07] ******<NSThread: 0x7134fa0>{name = (null), num = 1} // 主队列