使用NSOperationQueue簡化多線程開發


多線程開發是一件需要特別精心的事情,即使是對有多年開發經驗的工程師來說。

為了能讓初級開發工程師也能使用多線程,同時還要簡化復雜性。各種編程工具提供了各自的辦法。對於iOS來說,建議在盡可能的情況下避免直接操作線程,使用比如NSOperationQueue這樣的機制。

可以把NSOperationQueue看作一個線程池,可往線程池中添加操作(NSOperation)到隊列中。線程池中的線程可看作消費者,從隊列中取走操作,並執行它。

你可以設置線程池中只有一個線程,這樣,各個操作就可以認為是近似的順序執行了。為什么說是近似呢,后面會做解釋。

 

編寫最簡單的示例

先寫個最簡單的示例。

 

編寫一個NSOperation的子類,只需實現main方法。這里非常類似Java的Thread,你可以繼承它,並覆蓋run方法,在該方法里面寫入需要執行的代碼。這里的main方法和run方法作用是相似的。

頭文件:

@interface MyTask : NSOperation { 
    int operationId; 
}

@property int operationId;

@end

這里的operationId屬性不是必須的,是我想在后面標識區分多個Task的標識位。

m文件:

@implementation MyTask

@synthesize operationId;

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

@end

這里模擬了一個耗時10秒鍾的操作。

下面需要把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秒鍾后,日志打印完畢,類似這樣:

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)增加多個操作,比如這樣:

- (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,也就是沒有限制,同時運行隊列中的全部操作。

 

 

另附代碼實例:

只有代碼,解釋以后加上:

#import <UIKit/UIKit.h>

@interface tOQViewController : UIViewController {
    NSOperationQueue *myOQ;
}
-(void)doSomething;
@end

 

 

實現文件如下:

#import "tOQViewController.h"

@implementation tOQViewController
-(void)doSomething
{
    NSLog(@"in thread");
}
- (void)dealloc
{
    [super dealloc];
}

- (void)didReceiveMemoryWarning
{
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
    
    // Release any cached data, images, etc that aren't in use.
}

#pragma mark - View lifecycle


// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
    [super viewDidLoad];
    myOQ = [[NSOperationQueue alloc] init];
    [myOQ setMaxConcurrentOperationCount:1];
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doSomething) object:nil];
    [myOQ addOperation:op];
    [op release];
}


- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

@end


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM