在Object-c中,常用的多线程处理类分别为NSThread和NSOperationQueue。使用这两个类处理多线程,让复杂的多线程处理变得简单高效。下面分别介绍。


一、NSThread类

苹果公司的Cocoa框架共支持三种多线程机制,分别为NSThread、GCD(Grand Central Dispatch)、Cocoa NSOperatio。NSThree是官方推荐的线程处理方式,它在处理机制上,需要开发者负责手动管理Thread的生命周期,包括子线程与主线程之间的同步等。线程共享同一应用程序的部分内存空间,它们拥有对数据相同的访问权限。你得协调多个线程 对同一数据的访问,一般做法是在访问之前加锁,这会导致一定的性能开销。在 iOS 中我们可以使用多种形式的 thread。初次接触的读者也许会觉得生涩,但事实上NSThread类已经提供了完善的接口,在使用方式上并不会太难。使用NSTread管理线程,主要的步骤如下。

1)声明一个NSCondition同步锁;

2)声明若干个NSThread子线程;

3)指定NSThread子线程的目标指行方法(可以在构造函数中指定);

4)设置子线程的名称;

5)star启动子线程。

其中,子线程的执行方法一般只需要一个共同方法即可(可以通过线程名,分辨当前的执行线程)。下面通过代码演示NSThread类的使用。假设我们需要下载网络图片,在非异步形式的情况下,IOS界面必须等到图片下载完毕后,UI才会反应。这时利用多线程就可以达到异步下载的效果。

ViewController1.h


#import 
@interface ViewController1 : UIViewController{
    NSCondition *_condition;
    NSThread *_thread_1;
    NSThread *_thread_2;
}
@end


ViewController1.m

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82


/
//  ViewController1.m
//  Thread_demo
//
//  Created by MAC on 13-4-22.
//  Copyright (c) 2013年 李开涌. All rights reserved.
//
#import "ViewController1.h"
@interface ViewController1 ()
@end
@implementation ViewController1
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
        self.title=@"多线程NSThread";
    }
    return self;
}
- (void)viewDidLoad
{
    [super viewDidLoad];
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
    //创建一个进程锁,实现线程同步
    _condition=[[NSCondition alloc] init];
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
    //创建线程1
    _thread_1=[[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil];
    [_thread_1 setName:@"thread_1"];
    [_thread_1 start];
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
    //创建线程2
    _thread_2=[[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil];
    [_thread_2 setName:@"thread_2"];
    [_thread_2 start];
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
}
#pragma mark--线程执行方法
-(void)run{
    //进程上锁
    [_condition lock];
    NSLog(@"运行的线程名:%@",[[NSThread currentThread]name]);
    if ([[[NSThread currentThread]name] isEqualToString:@"thread_1"]) {
        NSLog(@"111");
        NSURL *p_w_picpathUrl_1=[NSURL URLWithString:@"http://t.beauty-soft.net/upload/ceiba20130224_014522_8679.jpg"];
        NSData *data_1=[NSData dataWithContentsOfURL:p_w_picpathUrl_1];
        UIImage *p_w_picpath_1=[UIImage p_w_picpathWithData:data_1];
        UIImageView *p_w_picpathView_1=[[UIImageView alloc] initWithImage:p_w_picpath_1];
        p_w_picpathView_1.frame=CGRectMake(60, 20, 200, 200);
        [self.view addSubview:p_w_picpathView_1];
        [p_w_picpathView_1 release];
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
    }else if([[[NSThread currentThread]name] isEqualToString:@"thread_2"]){
        NSLog(@"222");
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
        NSURL *p_w_picpathUrl_2=[NSURL URLWithString:@"http://t.beauty-soft.net/upload/ceiba20120704_121437_3455.jpg"];    
        NSData *data_2=[NSData dataWithContentsOfURL:p_w_picpathUrl_2];
        UIImage *p_w_picpath_2=[UIImage p_w_picpathWithData:data_2];
        UIImageView *p_w_picpathView_2=[[UIImageView alloc] initWithImage:p_w_picpath_2];
        p_w_picpathView_2.frame=CGRectMake(60, 250, 200, 200);
        [self.view addSubview:p_w_picpathView_2];
        [p_w_picpathView_2 release];
    }
    //解锁
    [_condition unlock];
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
}
-(void)dealloc{
    [_thread_1 release];
    [_thread_2 release];
    [_condition release];
    [super dealloc];
}
- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end



二、NSOperationQueue类

如果需要让线程同时并行运行多个,可以将线程加入队列(Queue)中,NSOperationQueue类就是一个线程队列管理类,他提供了线程并行、队列的管理。可以认为NSOperationQueue就是一个线程管理器,通过addOperations方法,我们可以一次性把多个(数组形式)线程添加到队列中。同时,NSOperationQueue允许通过setMaxConcurrentOperationCount方法设置队列的并行(同一时间)运行数量。NSOperationQueue的使用步骤如下。

1)声明一个NSOperationQueue对象;

2)声明若干个NSInvocationOperation子线程对象,并指定回调方法;

3)将NSInvocationOperation子线程添加到数组;

4)把数组赋给NSOperationQueue类中的addOperations方法;

5) 实现回调方法;

6)在回调方法中实现performSelectorOnMainThread方法,更新主线程上的界面UI元素。


下面,使用NSOperationQueue类,实现前面NSThread类相同的功能(即下载图片)。代码如下。


ViewController2.m


//

//  ViewController2.m
//  Thread_demo
//
//  Created by MAC on 13-4-22.
//  Copyright (c) 2013年 李开涌. All rights reserved.
//
#import "ViewController2.h"
@interface ViewController2 ()
@end
@implementation ViewController2
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
        self.title=@"线程队列";
    }
    return self;
}
- (void)viewDidLoad
{
    [super viewDidLoad];
    // 线程队列管理器,实现线程并行运行
    _opeartionQueue=[[NSOperationQueue alloc] init];
    //队列最大同时运行数量
    [_opeartionQueue setMaxConcurrentOperationCount:1];
                                                                                                                                                          
    tags=20;
    //创建线程1
    NSInvocationOperation *invocationOpearation_1=[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run:) object:@"http://t.beauty-soft.net/upload/ceiba20130224_014522_8679.jpg"] autorelease];
    //创建线程2
    NSInvocationOperation *invocationOpearation_2=[[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run:) object:@"http://t.beauty-soft.net/upload/ceiba20120704_121437_3455.jpg"] autorelease];
                                                                                                                                                          
    NSArray *array=@[invocationOpearation_1,invocationOpearation_2];
    //把程序添加到管理器中
    [_opeartionQueue addOperations:array waitUntilFinished:YES];
}
-(void)run:(NSString *)p_w_picpathStrinUrl{
    NSLog(@"%@",[_opeartionQueue operations]);
    NSLog(@"mainQueue%@",[NSOperationQueue mainQueue]);
    NSLog(@"i==%i",tags);
    NSLog(@"urlStr:%@",p_w_picpathStrinUrl);
    NSURL *url=[NSURL URLWithString:p_w_picpathStrinUrl];
    NSData *data=[NSData dataWithContentsOfURL:url];
    UIImage *p_w_picpath=[UIImage p_w_picpathWithData:data];
    UIImageView *p_w_picpathView=[[[UIImageView alloc] initWithImage:p_w_picpath]autorelease];
    p_w_picpathView.frame=CGRectMake(60, tags, 200, 200);
    tags+=200;
    //通知主线程更新UI
    [self performSelectorOnMainThread:@selector(updateUI:) withObject:p_w_picpathView waitUntilDone:NO];
                                                                                                                                                          
}
-(void)updateUI:(UIImageView *)p_w_picpathView{
                                                                                                                                                          
    [self.view addSubview:p_w_picpathView];
                                                                                                                                                          
}
- (void)dealloc
{
    [_opeartionQueue release];
    [super dealloc];
}
- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}
@end



[self performSelectorOnMainThread:@selector(updateLabel:)withObject:updateData waitUntilDone:YES]; 该方法是一个主线程同步方法,不仅可以应用在NSOpeartionQueue中,同样还适用于NSThread类。


Grand Central Dispatch,或者简称 GCD,是一个与 Block Object 产生工作的低级的 C API。GCD 真正的用途是将任务分配到多个核心又不让程序员担心哪个内核执行哪个任务。 在 Max OS X 上,多内核设备,包括笔记本,用户已经使用了相当长的时间。通过多核设备 比如 iPad2 的介绍,程序员能为 iOS 写出神奇的多核多线程 APP。


一、线程管理常用方法


1.performSelector(InBackground or MainThread)
这个方法比较方便,但是问题在于参数传递只能支持一个对象(传多个参数,我是将其打包在一个NSDictionary里面)
2.NSOperationQueue
这个方法稍微复杂,提供了每个任务的封装(NSOperation)。可以继承NSOperation之后,在main函数中写一些同步执行的代码,然后放到一个Queue之中,Queue自动管理Operation的执行和调度(在UI线程以外)。对于异步执行的代码,需要重载NSOperation的好几个函数才能正常工作(告诉Queue关于这个任务的进度以及执行情况)。
3.NSThread
轻量级的线程管理机制
4.GCD
在UI线程和其它线程之间切换很方便,可以和NSOperationQueue搭配使用。本文着重介绍这个方法。

5,pthread


二、GCD使用示例

GCD 的核心是分派队列。不论在 iOS 还是 Max OS X 分派队列,正如我们快看到的是 由位于主操作系统的 GCD 来管理的线程池。你不会直接与线程有工作关系。你只在分派队 列上工作,将任务分派到这个队列上并要求队列来调用你的任务。GCD 为运行任务提供了 几个选择:同步执行、异步执行和延迟执行等。


1、网络下载

以点击一个按钮,然后显示loading,同时在后台下载一张图片,最后将下载的图片放到UIImageView中显示为例。



//

显示loading
  self.indicator.hidden = NO;
  [self.indicator startAnimating];
  //进入异步线程
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
      //异步下载图片
      NSURL * url = [NSURL URLWithString:@"http://anImageUrl"];
      NSData * data = [NSData dataWithContentsOfURL:url];
      //网络请求之后进入主线程
      dispatch_async(dispatch_get_main_queue(), ^{
          //关闭loading
          [self.indicator stopAnimating];
          self.indicator.hidden = YES;
          if (data) {//显示图片
              self.p_w_picpathView.p_w_picpath = [UIImage p_w_picpathWithData:data];
          }
      });
  });


2、延迟执行


//

延迟2秒执行:
    double delayInSeconds = 2.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        // code to be executed on the main queue after delay
    });



3、自定义GCD

可以使用dispatch_queue_create函数创建自定义的GCD



dispatch_queue_t custom_queue = dispatch_queue_create(“customQueue”, NULL);
    dispatch_async(custom_queue, ^{
        //doing something in custom_queue
    });
    dispatch_release(custom_queue);



4、线程合并

利用GCD并行多个线程并且等待所有线程结束之后再执行其它任务



dispatch_group_t group = dispatch_group_create();
   dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
       // 并行执行的线程一
   });
   dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{
       // 并行执行的线程二
   });
   dispatch_group_notify(group, dispatch_get_global_queue(0,0), ^{
       // 汇总结果
   });