2009-10-29 48 views
5

我有一個包含2個NSOperations和設置陸續執行它們一個通過設置setMaxConcurrentOperationCount爲1問題隊列併發及非併發NSOperations

之一的操作中的是一個標準的非併發操作(一個NSOperationQueue只是一個main方法),它同步從網絡檢索一些數據(當然在單獨的操作線程上)。另一個操作是併發操作,因爲我需要使用一些必須異步運行的代碼。

問題是,我發現併發操作只有在它被首先添加到隊列中時才起作用。如果它出現在任何非併發操作之後,那麼奇怪的是start方法被調用的很好,但在該方法結束後,我已經建立了連接到回調方法,但它從來沒有。之後沒有進一步的隊列操作被執行。就好像它在啓動方法返回後掛起,並且不會調用任何URL連接的回調!

如果我的併發操作先放在隊列中,那麼它一切正常,異步回調工作,並在完成後執行後續操作。我根本聽不懂!

你可以在下面看到我的併發NSOperation的測試代碼,我非常肯定它是穩定的。

任何幫助將不勝感激!

主線程觀察:

我剛纔發現,如果同時操作是先在隊列中,則[start]方法被稱爲主線程。但是,如果它不是第一個在隊列中(如果它是在併發或非併發之後),那麼在主線程上不會調用方法[start]。這看起來很重要,因爲它符合我的問題模式。這可能是什麼原因?

併發的NSOperation代碼:

@interface ConcurrentOperation : NSOperation { 
    BOOL executing; 
    BOOL finished; 
} 
- (void)beginOperation; 
- (void)completeOperation; 
@end 

@implementation ConcurrentOperation 
- (void)beginOperation { 
    @try { 

     // Test async request 
     NSURLRequest *r = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://www.google.com"]]; 
     NSURLConnection *c = [[NSURLConnection alloc] initWithRequest:r delegate:self]; 
     [r release]; 

    } @catch(NSException * e) { 
     // Do not rethrow exceptions. 
    } 
} 
- (void)connectionDidFinishLoading:(NSURLConnection *)connection { 
    NSLog(@"Finished loading... %@", connection); 
    [self completeOperation]; 
} 
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { 
    NSLog(@"Finished with error... %@", error); 
    [self completeOperation]; 
} 
- (void)dealloc { 
    [super dealloc]; 
} 
- (id)init { 
    if (self = [super init]) { 

     // Set Flags 
     executing = NO; 
     finished = NO; 

    } 
    return self; 
} 
- (void)start { 

    // Main thread? This seems to be an important point 
    NSLog(@"%@ on main thread", ([NSThread isMainThread] ? @"Is" : @"Not")); 

    // Check for cancellation 
    if ([self isCancelled]) { 
     [self completeOperation]; 
     return; 
    } 

    // Executing 
    [self willChangeValueForKey:@"isExecuting"]; 
    executing = YES; 
    [self didChangeValueForKey:@"isExecuting"]; 

    // Begin 
    [self beginOperation]; 

} 

// Complete Operation and Mark as Finished 
- (void)completeOperation { 
    BOOL oldExecuting = executing; 
    BOOL oldFinished = finished; 
    if (oldExecuting) [self willChangeValueForKey:@"isExecuting"]; 
    if (!oldFinished) [self willChangeValueForKey:@"isFinished"]; 
    executing = NO; 
    finished = YES; 
    if (oldExecuting) [self didChangeValueForKey:@"isExecuting"]; 
    if (!oldFinished) [self didChangeValueForKey:@"isFinished"]; 
} 

// Operation State 
- (BOOL)isConcurrent { return YES; } 
- (BOOL)isExecuting { return executing; } 
- (BOOL)isFinished { return finished; } 

@end 

排隊代碼

// Setup Queue 
myQueue = [[NSOperationQueue alloc] init]; 
[myQueue setMaxConcurrentOperationCount:1]; 

// Non Concurrent Op 
NonConcurrentOperation *op1 = [[NonConcurrentOperation alloc] init]; 
[myQueue addOperation:op1]; 
[op1 release]; 

// Concurrent Op 
ConcurrentOperation *op2 = [[ConcurrentOperation alloc] init]; 
[myQueue addOperation:op2]; 
[op2 release]; 

回答

10

我發現了什麼問題!

這兩個無價文章由Dave Dribin描述很詳細的併發操作,以及雪豹&的iPhone SDK調用異步的東西,需要一個運行循環時引入的問題。

http://www.dribin.org/dave/blog/archives/2009/05/05/concurrent_operations/ http://www.dribin.org/dave/blog/archives/2009/09/13/snowy_concurrent_operations/

感謝Chris蘇特也指着我在正確的方向!

它的關鍵是要確保start方法我們在主線程中調用:

- (void)start { 

    if (![NSThread isMainThread]) { // Dave Dribin is a legend! 
     [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO]; 
     return; 
    } 

    [self willChangeValueForKey:@"isExecuting"]; 
    _isExecuting = YES; 
    [self didChangeValueForKey:@"isExecuting"]; 

    // Start asynchronous API 

} 
+0

我也有這個相同的問題。但在我的情況下,啓動方法有時候也不需要新添加的操作。隊列仍顯示狀態'正在運行'。所以上面的方法不能使用。你知道任何其他解決方案嗎?請幫忙.. – 2013-06-12 06:59:55

0

我沒有注意到,我也不看addDependency:任何提及,這似乎像一個先決條件讓操作按正確的順序執行。

總之,第二個操作取決於第一個。

+0

噢,是我忘了提及有關隊列代碼。我沒有添加任何依賴項,但沒有任何理由。所有的操作都是不相關的,但我只是希望他們一個接一個地運行。依賴關係會對我遇到的問題產生什麼影響?我已經將setMaxConcurrentOperationCount設置爲1,並認爲這已經足夠了。感謝您的答覆! – 2009-10-29 10:02:16

+0

我剛剛添加了依賴關係,它對我的​​問題沒有任何影響。 – 2009-10-29 10:40:12

6

你的問題很可能與NSURLConnection。 NSURLConnection取決於運行特定模式的運行循環(通常只是默認模式)。

有一些你的問題的解決方案:

  1. 確保此操作只運行在主線程上。如果你在OS X上這樣做,你會希望檢查它在所有運行循環模式(例如模態和事件追蹤模式)下都能達到你想要的效果,但我不知道iPhone上的交易是什麼。

  2. 創建和管理您自己的線程。不是一個好的解決方案

  3. 調用 - [NSURLConnection scheduleInRunLoop:forMode:]並傳入主線程或其他您知道的線程。如果你這樣做,你可能會首先調用 - [NSURLConnection unscheduleInRunLoop:forMode:],否則你可能會在多個線程中接收數據(或者至少這是文檔中的建議)。

  4. 使用類似於+ [NSData dataWithContentsOfURL:options:error:]的東西,這也將簡化您的操作,因爲您可以改爲非併發操作。

  5. #4的變體:使用+ [NSURLConnection sendSynchronousRequest:returningResponse:error:]。

如果你能擺脫它,做#4或#5。

+0

感謝您的回覆。我發佈的代碼只是我爲測試目的創建的一個簡單示例。實際的程序使用了一個不受我控制的API,並且本質上是異步的。 但是你確實讓我正確地重新閱讀了兩篇有趣的文章,它們已經解決了這個問題!我會發佈一個答案,以便在其他人遇到問題時很清楚,但是要感謝您抽出時間,並且對運行循環感到絕望! – 2009-10-29 13:31:57