0

我正在查看Ray Wenderlich教程,介紹使用分派隊列在一組任務完成時得到通知。 http://www.raywenderlich.com/63338/grand-central-dispatch-in-depth-part-2dispatch_group_wait()dispatch_group_create()和dispatch_group_enter()從不同的隊列中調用時的行爲需要說明

在「代碼工作」下顯示的第一個代碼直接來自教程。警報視圖(最終完成塊)在所有3次下載完成後執行。

我試着玩弄它,並在「不工作的代碼」中將異步向下移動,以查看dispatch_group_create()和dispatch_group_enter()發生在不同隊列中會發生什麼。在這種情況下,dispatch_group_enter()似乎沒有註冊,因爲即使在所有下載完成之前,dispatch_group_wait()立即完成並且警報視圖(最終完成塊)被執行。

有人可以解釋什麼發生在第二個案件? (這只是爲了我的理解dispatch組是如何工作的,我意識到這最好是將整個函數放在全局併發隊列中以避免阻塞主線程)。

的作品

- (void)downloadPhotosWithCompletionBlock:(BatchPhotoDownloadingCompletionBlock)completionBlock 
{ 

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),^{ 

    __block NSError *error; 
    dispatch_group_t downloadGroup = dispatch_group_create(); 

    for (NSInteger i = 0; i < 3; i++) 
    { 
     NSURL *url; 
     switch (i) { 
      case 0: 
       url = [NSURL URLWithString:kOverlyAttachedGirlfriendURLString]; 
       break; 
      case 1: 
       url = [NSURL URLWithString:kSuccessKidURLString]; 
       break; 
      case 2: 
       url = [NSURL URLWithString:kLotsOfFacesURLString]; 
       break; 
      default: 
       break; 
     } 


      dispatch_group_enter(downloadGroup); 
      __block Photo *photo = [[Photo alloc] initwithURL:url 
            withCompletionBlock:^(UIImage *image, NSError *_error) { 
             if (_error) { 
              error = _error; 
             } 
             NSLog(@"Finished completion block for photo alloc for URL %@ and photo is %@",url,photo) ; 
             dispatch_group_leave(downloadGroup); 
            }]; 

      [[PhotoManager sharedManager] addPhoto:photo]; 
      NSLog(@"Finished adding photo to shared manager for URL %@ and photo is %@",url,photo) ; 
    } 

    dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER); // 5 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     if (completionBlock) { 
      NSLog(@"Executing completion block after download group complete") ; 
      completionBlock(error); 
     } 
    }) ; 
    }) ; 
} 

編輯的代碼

代碼,不具有額外的NSLog語句

代碼的工作,不工作

- (void)downloadPhotosWithCompletionBlock:(BatchPhotoDownloadingCompletionBlock)completionBlock 
{ 

__block NSError *error; 
dispatch_group_t downloadGroup = dispatch_group_create(); 

for (NSInteger i = 0; i < 3; i++) 
{ 
    NSURL *url; 
    switch (i) { 
     case 0: 
      url = [NSURL URLWithString:kOverlyAttachedGirlfriendURLString]; 
      break; 
     case 1: 
      url = [NSURL URLWithString:kSuccessKidURLString]; 
      break; 
     case 2: 
      url = [NSURL URLWithString:kLotsOfFacesURLString]; 
      break; 
     default: 
      break; 
    } 

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),^{ 
     dispatch_group_enter(downloadGroup); 
     NSLog(@"Enetered group for URL %@",url) ; 
     __block Photo *photo = [[Photo alloc] initwithURL:url 
             withCompletionBlock:^(UIImage *image, NSError *_error) { 
              if (_error) { 
               error = _error; 
              } 
              NSLog(@"Finished completion block for photo alloc for URL %@ and photo is %@",url,photo) ; 
              dispatch_group_leave(downloadGroup); 
             }]; 

     [[PhotoManager sharedManager] addPhoto:photo]; 
     NSLog(@"Finished adding photo to shared manager for URL %@ and photo is %@",url,photo) ; 
    }) ; 
} 

NSLog(@"Executing wait statement") ; 
dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER); // 5 
dispatch_async(dispatch_get_main_queue(), ^{ 
    if (completionBlock) { 
     NSLog(@"Executing completion block after download group complete") ; 
     completionBlock(error); 
    } 
}) ; 
} 

回答

3

dispatch_group_enter()似乎沒有註冊」,因爲在dispatch_group_wait()被調用時它還沒有被實際調用。或者,而不是保證被調用。有一個競爭條件。

這不是專門針對不同的隊列。它關於併發性和異步性。

dispatch_async()只是意味着「向列表中添加任務」,隱含的理解是,某個地方某些時候會將任務從該列表中除去並執行它們。它將任務放入列表後立即返回給調用者。它不會等待任務開始運行,更不用說完成運行了。

因此,您的for循環運行速度非常快,當它退出時,它可能已排隊的任務已啓動。或者,如果有的話,可能是他們還沒有完成進入該組。

在任何事情進入該組之前,您的代碼可能會完成對dispatch_group_wait()的呼叫。

通常,在撥打電話dispatch_group_wait()之前,您要確保所有相關的致電dispatch_group_enter()的電話已完成。最簡單的方法是讓它們在一個執行環境中同步發生。也就是說,不要在異步調度的塊內部調用dispatch_group_enter()

+0

謝謝,這是有道理的。請參閱上面的修改。我看到在「dispatch_wait」之前的NSLog語句之前執行「group_enter」之後的NSLog語句。這似乎指出了除上述解釋以外的一些根本原因。我應該使用斷點進行調試嗎? Xcode中的NSLog輸出是2016-02-21 21:37:01.155 GooglyPuff [393:136916]這是你的外在聲音:我是在內部聲音之前還是之後打印? 2016-02-21 21:37:05.319 GooglyPuff [393:136937]網址爲http://i.imgur.com/UvqEgCv.png 2016-02-21 21:37:05.319 GooglyPuff [393:136916 ]執行等待語句 –

+0

剛剛執行了斷點。首先執行1st dispatch_group_enter,然後dispatch_group_wait,然後剩下兩個dispatch_group_enter。在dispatch_group_wait調用期間dispatch_group_enter的執行和狀態更改被反映之間是否存在延遲?添加最小延遲([NSThread sleepForTimeInterval:0.001];)將消除競態條件,並且事情按照預期的順序發生。 –

+0

在您的問題中輸入一個沒有斷點的運行輸出。我沒有按照上面評論中的內容。增加延遲可能會減少競爭條件的窗口,但不一定會消除它。競爭條件是程序邏輯的問題,而不僅僅是執行的變幻莫測(比如系統是否忙或調度器打嗝等等)。 –