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

文章出處:http://marshal.easymorse.com/archives/4519

 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章