iOS多執行緒的初步研究(三)-- NSRunLoop

憶江南的部落格發表於2015-07-28

弄清楚NSRunLoop確實需要花時間,這個類的概念和模式似乎是Apple的平臺獨有(iOS+MacOSX),很難徹底搞懂(iOS沒開源,嗚嗚)。

官網的解釋是說run loop可以用於處理非同步事件,很抽象的說法。不羅嗦,先看看NSRunLoop幾個常用的方法。

+ (NSRunLoop *)currentRunLoop; //獲得當前執行緒的run loop

+ (NSRunLoop *)mainRunLoop; //獲得主執行緒的run loop

- (void)run; //進入處理事件迴圈,如果沒有事件則立刻返回。注意:主執行緒上呼叫這個方法會導致無法返回(進入無限迴圈,雖然不會阻塞主執行緒),因為主執行緒一般總是會有事件處理。

- (void)runUntilDate:(NSDate *)limitDate; //同run方法,增加超時引數limitDate,避免進入無限迴圈。使用在UI執行緒(亦即主執行緒)上,可以達到暫停的效果。

- (BOOL)runMode:(NSString *)mode beforeDate:(NSDate *)limitDate; //等待訊息處理,好比在PC終端視窗上等待鍵盤輸入。一旦有合適事件(mode相當於定義了事件的型別)被處理了,則立刻返回;類同run方法,如果沒有事件處理也立刻返回;有否事件處理由返回布林值判斷。同樣limitDate為超時引數。

- (void)acceptInputForMode:(NSString *)mode beforeDate:(NSDate *)limitDate; //似乎和runMode:差不多(測試過是這種結果,但確定是否有其它特殊情況下的不同),沒有BOOL返回值。

官網文件也提到run和runUntilDate:會以NSDefaultRunLoopMode引數呼叫runMode:來處理事件。

當app執行後,iOS系統已經幫助主執行緒啟動一個run loop,而一般執行緒則需要手動來啟動run loop。

使用run loop的一個好處就是避免執行緒輪詢的開銷,run loop在無事件處理時可以自動進入睡眠狀態,降低CPU的能耗。

比如一般執行緒輪詢的方式為:

while (condition)

{

  // waiting for new data

  sleep(1);

  // process current data

}

其實這種方式是很能消耗CPU時間片的,如果在UI執行緒中這樣使用還會阻塞UI響應。而改用NSRunLoop來實現,則可大大改善執行緒的執行效率,而且不會阻塞UI(很神奇,呵呵。有點像javascript,用單執行緒實現多執行緒的效果)。上面的例子可以改為:

while (condition)

{

  // waiting for new data

  if ([[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]])

  {

    // process current data

  }

}

 

接下來我們看看具體的例子,包括如何實現執行緒執行的關聯同步(join),以及UI執行緒run loop的一般使用技巧等。

假設有個執行緒A,它會啟動執行緒B,然後等待B執行緒的結束。NSThread是沒有join的方法,用run loop方式實現就比較精巧。

NSThread *A; //global

A = [[NSThread alloc] initWithTarget:self selector:@selector(runA) object:nil]; //生成執行緒A

[A start]; //啟動執行緒A

- (void)runA

{

  [NSThread detachNewThreadSelector:@selector(runB) toTarget:self withObject:nil]; //生成執行緒B

  while (1)

  {

    if ([[NSRunLoop currentRunLooprunMode:@"CustomRunLoopMode" beforeDate:[NSDate distantFuture]]) //相當於join

    {

      NSLog(@"執行緒B結束");

      break;

    }

  }

}

- (void)runB

{

  sleep(1);

  [self performSelector:@selector(setData) onThread:A withObject:nil waitUntilDone:YES modes:@[@"CustomRunLoopMode"]];

}

實際執行時,過1秒後執行緒A也會自動結束。這裡用到自定義的mode,一般在UI執行緒上呼叫run loop會使用預設的mode。結合while迴圈,UI執行緒就可以實現子執行緒的同步執行(具體例子這裡不再描述,可參看:http://www.cnblogs.com/tangbinblog/archive/2012/12/07/2807088.html)。

下面羅列呼叫主執行緒的run loop的各種方式,讀者可以加深理解:

[[NSRunLoop mainRunLoop] run]; //主執行緒永遠等待,但讓出主執行緒時間片

[[NSRunLoop mainRunLooprunUntilDate:[NSDate distantFuture]]; //等同上面呼叫

[[NSRunLoop mainRunLooprunUntilDate:[NSDate date]]; //立即返回

[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10.0]]; //主執行緒等待,但讓出主執行緒時間片,然後過10秒後返回

[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate: [NSDate distantFuture]]; //主執行緒等待,但讓出主執行緒時間片;有事件到達就返回,比如點選UI等。

[[NSRunLoop mainRunLooprunMode:NSDefaultRunLoopMode beforeDate: [NSDate date]]; //立即返回

[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate: [NSDate dateWithTimeIntervalSinceNow:10.0]]; //主執行緒等待,但讓出主執行緒時間片;有事件到達就返回,如果沒有則過10秒返回。

 

相關文章