IOS多线程(NSOperation,NSOperationQueue)

 

 
  1. 含义:NSOperation,NSOperationQueue是什么。

     The NSOperation class is an abstract class you use to encapsulate the code and data associated with a single task.NSOperation是一个你需要将代码和数据放在一个单任务中执行的抽象类。

     The NSOperationQueue class regulates the execution of a set of NSOperation objects.NSOperationQueue是用来管理多个NSOperation对象的一个类。

     2.使用方法

     使用 NSOperation的方式有两种,

  1. 一种是用定义好的两个子类:

       NSInvocationOperation 和 NSBlockOperation。

复制代码
@interface ViewController ()  
  
@end  
  
@implementation ViewController  
  
- (void)viewDidLoad  
{  
    [super viewDidLoad];  
    NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self  
                                                                           selector:@selector(downloadImage:)  
                                                                             object:kURL];  
      
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];  
    [queue addOperation:operation];  
    // Do any additional setup after loading the view, typically from a nib.  
}  
  
-(void)downloadImage:(NSString *)url{  
    NSLog(@"url:%@", url);  
    NSURL *nsUrl = [NSURL URLWithString:url];  
    NSData *data = [[NSData alloc]initWithContentsOfURL:nsUrl];  
    UIImage * image = [[UIImage alloc]initWithData:data];  
    [self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:YES];  
}  
-(void)updateUI:(UIImage*) image{  
    self.imageView.image = image;  
}  
复制代码

  2.另一种则是继承NSOperation,重写NSOperation的main方法

  编写一个NSOperation的子类,只需实现main方法。这里非常类似Java的Thread,你可以继承它,并覆盖run方法,在该方法里面写入需要执行的代码。这里的main方法和run方法作用是相似的。

复制代码
@interface MyTask : NSOperation { 
    int operationId; 
}

@property int operationId;

@end
复制代码

这里的operationId属性不是必须的,是我想在后面标识区分多个Task的标识位。

复制代码
@implementation MyTask

@synthesize operationId;

- (void)main{ 
    NSLog(@"task %i run … ",operationId); 
    [NSThread sleepForTimeInterval:10]; 
    NSLog(@"task %i is finished. ",operationId); 
}

@end
复制代码

这里模拟了一个耗时10秒钟的操作。

2011-07-18 15:59:14.622 MultiThreadTest[24271:6103] task 1 run … 
2011-07-18 15:59:24.623 MultiThreadTest[24271:6103] task 1 is finished.

可以向操作队列(NSOperationQueue)增加多个操作,比如这样:

下面需要把Task加入到队列中:

复制代码
- (void)viewDidLoad { 
    [super viewDidLoad]; 
    queue=[[NSOperationQueue alloc] init]; 
    
    int index=1; 
    MyTask *task=[[[MyTask alloc] init] autorelease]; 
    task.operationId=index++; 
         
    [queue addOperation:task];
复制代码

我直接找了个Controller的方法写上了。运行结果是,界面出现了,而task还未执行完,说明是多线程的。10秒钟后,日志打印完毕,类似这样:

复制代码
- (void)viewDidLoad { 
    [super viewDidLoad]; 
    queue=[[NSOperationQueue alloc] init]; 
    
    int index=1; 
    MyTask *task=[[[MyTask alloc] init] autorelease]; 
    task.operationId=index++;     
    [queue addOperation:task]; 
    
    task=[[[MyTask alloc] init] autorelease]; 
    task.operationId=index++;

    [queue addOperation:task]; 
}
复制代码

那么打印出的内容是不定的,有可能是这样:

2011-07-18 15:49:48.087 MultiThreadTest[24139:6203] task 1 run … 
2011-07-18 15:49:48.087 MultiThreadTest[24139:1903] task 2 run … 
2011-07-18 15:49:58.122 MultiThreadTest[24139:6203] task 1 is finished. 
2011-07-18 15:49:58.122 MultiThreadTest[24139:1903] task 2 is finished.

甚至有可能是这样:

2011-07-18 15:52:24.686 MultiThreadTest[24168:1b03] task 2 run … 
2011-07-18 15:52:24.685 MultiThreadTest[24168:6003] task 1 run … 
2011-07-18 15:52:34.708 MultiThreadTest[24168:1b03] task 2 is finished. 
2011-07-18 15:52:34.708 MultiThreadTest[24168:6003] task 1 is finished.

因为两个操作提交的时间间隔很近,线程池中的线程,谁先启动是不定的。

那么,如果需要严格意义的顺序执行,怎么办呢?

处理操作之间的依赖关系

如果操作直接有依赖关系,比如第二个操作必须等第一个操作结束后再执行,需要这样写:

复制代码
queue=[[NSOperationQueue alloc] init];

int index=1; 
MyTask *task=[[[MyTask alloc] init] autorelease]; 
task.operationId=index++;

[queue addOperation:task];

task=[[[MyTask alloc] init] autorelease]; 
task.operationId=index++;

if ([[queue operations] count]>0) { 
    MyTask *theBeforeTask=[[queue operations] lastObject]; 
    [task addDependency:theBeforeTask]; 
}

[queue addOperation:task];
复制代码

这样,即使是多线程情况下,可以看到操作是严格按照先后次序执行的。

控制线程池中的线程数

可以通过类似下面的代码:

[queue setMaxConcurrentOperationCount:2];

来设置线程池中的线程数,也就是并发操作数。默认情况下是-1,也就是没有限制,同时运行队列中的全部操作。

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s