2014-01-21 90 views
2

我正在用下面的代碼解析XML文件,並得到奇怪的行爲。我希望用戶能夠排隊多個需要解析的文件,並將它們放在一個單獨的線程上並串行完成。一切工作如預期如果我派遣一個隊列,並允許它完成點擊下一個。但是,如果我從第一個隊列開始並立即觸發另一個隊列,則第一個隊列完成解析,但通過調用Main Q來使UI更新和應用程序變得沒有響應(儘管我的微調繼續前進),但並未完成該塊。根據Xcode,CPU下降到0%,內存保持不變。dispatch_async沒有完成塊

我正在使用CoreData並在各自的線程上創建新的MOC。再說一次,如果我一次只做一個,它可以正常工作,如果我註釋掉解析並只做一個循環,我可以對它進行排隊並工作,在完成之前開始下一個循環。

這篇文章提到主線程被阻塞,但是如果我運行重複計時器並在隊列運行時將其註銷,所有都是好的。

參考 - > dispatch_async block on main queue is never execeuted

-(void)didSelectDownloadButtonForCell:(FieldsCell *)cell{ 

    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(ticktock) userInfo:nil repeats:YES]; 

    cell.shouldBeAnimating = YES; 
    cell.ActivityIndicator.hidden = NO; 
    [cell.ActivityIndicator startAnimating]; 
    cell.DownloadBtn.hidden = YES; 
    cell.PartialDownloadBtn.hidden = YES; 

    HNField *field = [HNField fieldWithField_id:[NSNumber numberWithInteger:cell.tag]]; 
    NSString *xmlfile = [NSString stringWithFormat:@"%@%@",[field.name stringByReplacingOccurrencesOfString:@" " withString:@""],@".xml"]; 
    NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:xmlfile]; 
    __block NSData *data = [[NSData alloc] initWithContentsOfFile:path]; 


    //load up core data with demo xml file 
    dispatch_async(self.backgroundq, ^(void){ 
     NSLog(@"Block dispatched"); 
     HNArchivePointParser *aParser = [[HNArchivePointParser alloc]init]; 
     [aParser parsethisdata:data]; 
     NSLog(@"Parser has finished"); 

//running simple loops works just fine. 
//for (int i = 1; i < 1000; i++) { 
// NSLog(@"%d - %@",i,cell); 
//} 


     dispatch_async(dispatch_get_main_queue(), ^{ 
      NSLog(@"THIS IS THE MAIN Q"); 
      cell.shouldBeAnimating = NO; 
      [cell.ActivityIndicator stopAnimating]; 
      cell.ActivityIndicator.hidden = YES; 
      cell.DownloadBtn.hidden = YES; 
      cell.PartialDownloadBtn.hidden = NO; 
     }); 
     NSLog(@"background Q completed"); 
    }); 
} 

-(void)ticktock{ 
    NSLog(@"tick"); 
} 

我懶洋洋地讓我的隊列。

-(dispatch_queue_t)backgroundq{ 
    if (!_backgroundq) { 
     NSLog(@"NEW"); 
     _backgroundq = dispatch_queue_create("com.myapp....", DISPATCH_QUEUE_SERIAL); 
    } 

    if ([NSThread isMainThread]) { 
     NSLog(@"RETURNED on Main Thread"); 
    }else NSLog(@"RETURNED on the wrong thread"); 

    return _backgroundq; 
} 

我可以忽略什麼?

更新14年1月23日 這是正確的假設didSelectDownloadButtonForCell:始終調用主線程上,我添加了一些代碼來確認。 我也更新了上面的代碼,這裏是我的日誌,當我點擊一個按鈕,並等待它完成之前單擊下一個。

2014-01-23 10:02:08.866[1362:70b] NEW 
2014-01-23 10:02:08.867[1362:70b] RETURNED on Main Thread 
2014-01-23 10:02:08.868[1362:f03] Block dispatched 
2014-01-23 10:02:09.864[1362:70b] tick 
2014-01-23 10:02:10.864[1362:70b] tick 
2014-01-23 10:02:11.864[1362:70b] tick 
2014-01-23 10:02:12.864[1362:70b] tick 
2014-01-23 10:02:13.719[1362:f03] Parser has finished 
2014-01-23 10:02:13.719[1362:70b] THIS IS THE MAIN Q 
2014-01-23 10:02:13.719[1362:f03] background Q completed 
2014-01-23 10:02:13.863[1362:70b] tick 
2014-01-23 10:02:14.864[1362:70b] tick 
2014-01-23 10:02:15.864[1362:70b] tick 
more ticks......... 

這裏是日誌當我點擊相同的按鈕,然後點擊下一步按鈕之前等待2個蜱

2014-01-23 10:10:32.484[1417:70b] NEW 
2014-01-23 10:10:32.485[1417:70b] RETURNED on Main Thread 
2014-01-23 10:10:32.486[1417:f03] Block dispatched 
2014-01-23 10:10:33.482[1417:70b] tick 
2014-01-23 10:10:34.481[1417:70b] tick 
2014-01-23 10:10:37.304[1417:f03] Parser has finished 
2014-01-23 10:10:37.304[1417:f03] background Q completed 

定時器是在主線程中如此明顯的鎖住(UI變得反應遲鈍太儘管我的微調繼續)。

最後這裏是我HNArchivePointParserparsethisdata其目的是在後臺線程中使用。

-(void)parsethisdata:(NSData *)data{ 

    self.clientid = [[NSUserDefaults standardUserDefaults] objectForKey:@"UserId"]; 

    //create a MOC in background thread 
    AppDelegate *app = (AppDelegate *)[[UIApplication sharedApplication]delegate]; 
    self.MOC = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
    [self.MOC setPersistentStoreCoordinator:[app persistentStoreCoordinator]]; 
    self.theResponseId = 0; 

    if (!self.parser) { 
     self.parser = [[NSXMLParser alloc]initWithData:data]; 
     [self.parser setDelegate:self]; 
     [self.parser parse]; 
    } 
} 
+1

您應該發佈您看到的控制檯輸出 - 例如,您是否曾經看到「Main Q」被記錄? FWIW,它可能並不重要,但是你的'backgroundq'的懶惰創建不是線程安全的。假設'didSelectDownloadButtonForCell:'總是在主線程上調用,那應該不重要,但是誰知道?在HNArchivePointParser中,如果有任何依賴關係/聯鎖涉及,也很難說出什麼。如果這個類不是線程安全的,或者只是假設它是主線程的,或者它是在runloop的上下文中使用的,那麼可以解釋你所看到的行爲。 – ipmcc

+0

@ipmcc感謝您的回覆!我已更新/添加代碼以幫助闡明更多的內容。我真的難以知道爲什麼這個代碼一次一個字,但當我嘗試排隊時,主線程鎖定了?我已經失去了很多睡眠! – kev

+0

您是否在後臺線程上使用'performBlock:'進行與'NSPrivateQueueConcurrencyType' MOC的所有交互?如果您設置了一個異常斷點,您是否在回調從未發生的情況下點擊它? – ipmcc

回答

0

很難知道究竟是什麼導致你的麻煩,但有一點是肯定的:你應該做的NSPrivateQueueConcurrencyTypeNSMainQueueConcurrencyType管理對象上下文的所有工作內提交performBlock:performBlockAndWait:塊。

如果您只想說「我保證此MOC上的所有操作都將從一個線程完成,並且這是我的責任」,那麼您應該使用NSConfinementConcurrencyType

我不能說出爲什麼它不會因爲沒有使用它而訪問它而死掉的原因(這很容易檢查),但是關於這個的docs are pretty clear

GCD對於這些事情一般都很聰明。例如,如果您調用performBlockAndWait:,它在大多數情況下最終會使用調用線程來執行實際執行,它將等待,直到沒有其他人在該隊列中執行,因此不會自動產生一些巨大的切換成本參與其中。

假設MOC對於該解析操作是私有的,沒有理由不僅僅在提交給performBlock:的單個塊中執行整個解析操作。