2012-09-11 97 views
3

我使用Amazon的iOS SDK(v1.3.2)將大文件上傳到AWS S3,我使用NSOperation的子類。這一切工作正常,但一些beta測試者遇到死鎖(iOS 5.1.1)。結果是操作被調度的NSOperationQueue被阻塞,因爲一次只允許一個操作運行。問題是我不能重現這個問題,而beta測試者每次都會遇到這個問題。NSOperation死鎖和塊NSOperationQueue

由於AWS iOS SDK的工作原理,操作非常複雜。但是,據我所知,根據我的測試,問題與AWS iOS SDK無關。操作的主要方法粘貼在下面。該操作的主要方法的想法是基於this Stack Overflow question

- (void)main { 
    // Operation Should Terminate 
    _operationShouldTerminate = NO; 

    // Notify Delegate 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     [self.delegate operation:self isPreparingUploadWithUuid:self.uuid]; 
    }); 

    // Increment Network Activity Count 
    [self incrementNetworkActivityCount]; 

    // Verify S3 Credentials 
    [self verifyS3Credentials]; 

    while (!_operationShouldTerminate) { 
     if ([self isCancelled]) { 
      _operationShouldTerminate = YES; 

     } else { 
      // Create Run Loop 
      [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; 
     } 
    } 

    // Decrement Network Activity Count 
    [self decrementNetworkActivityCount]; 

    NSLog(@"Operation Will Terminate"); 
} 

該定型多載設置布爾_operationShouldTerminateYES到終止操作的方法。該方法看起來像這樣。

- (void)finalizeMultipartUpload { 
    // Notify Delegate 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     [self.delegate operation:self didFinishUploadingUploadWithUuid:self.uuid]; 
    }); 

    // Operation Should Terminate 
    _operationShouldTerminate = YES; 

    NSLog(@"Finalize Multipart Upload"); 
} 

,最終的日誌語句打印到控制檯,但在main方法的while循環似乎並不如退出在操作的主要方法,最終的日誌語句不打印到控制檯。因此,調度操作的操作隊列被阻止,並且任何調度的操作都不會被執行。

該操作的isFinished方法簡單地返回_operationShouldTerminate,如下所示。

- (BOOL)isFinished { 
    return _operationShouldTerminate; 
} 

奇怪的是,while循環不退出,這是更奇怪的是,它不會在任何我自己的測試設備(iPhone 3GS,iPad的1和iPad 3)的發生。任何幫助或指針非常感謝。

回答

2

問題的解決方案既複雜又簡單,事實證明。我錯誤地認爲,操作的方法和委託回調是在同一個線程上執行的,也就是調用該操作的方法的線程。這並非總是如此。

儘管在我的測試和我的設備(iPhone 3GS)上這是真的,這也是我自己沒有經歷過這個問題的原因。但是,我的beta測試人員使用帶有多核處理器(iPhone 4/4S)的設備,導致一些代碼在與調用操作main方法的線程不同的線程上執行。

這樣做的結果是在錯誤的線程上finalizeMultipartUpload方法中修改了_operationShouldTerminate。這又意味着main方法的while循環未正確退出,導致操作死鎖。

簡而言之,解決方案是在調用main方法的同一線程上更新_operationShouldTerminate。這將正確退出while循環並退出操作。

1

有許多與你的代碼的問題,我可以提供兩種解決方案:

1)對蘋果的Concurrency Programming Guide併發NSOperations閱讀起來。要保持runLoop「活着」,您必須添加一個端口或安排一個計時器。主循環應該包含一個自動釋放池,因爲您可能不會得到一個(請參閱同一備忘錄中的內存管理)。您需要實施KVO,以便在操作完成時讓operationQueue知道。

2)或者,您可以採用small amount of field tested hardened code並重新使用它。這個Xcode項目包含三個你感興趣的類:一個ConcurrentOperation文件,它可以很好的完成上面的任務。 Webfetcher.m類顯示如何對併發操作進行子類化以從Web執行異步URL提取。 OperationsRunner是一個小的幫助程序文件,您可以將它添加到任何種類的類中來管理操作隊列(運行,取消,查詢等)。以上所有內容都少於100行代碼,併爲您提供了一個使代碼正常工作的基礎。 OperationsRunner.h文件也提供了「如何做」。

+0

謝謝你的回答大衛。併發編程指南對我來說並不陌生,對於這類問題確實是一個有用的指導。保持運行循環在這種情況下似乎不成問題。 –